Diff to HTML by rtfpessoa

Files changed (141) hide show
  1. .github/actions/install/action.yml +1 -1
  2. .github/workflows/benchmarks.yml +1 -1
  3. .github/workflows/build-linux-installer-deb.yml +4 -4
  4. .github/workflows/build-linux-installer-rpm.yml +5 -5
  5. .github/workflows/build-macos-installers.yml +4 -4
  6. .github/workflows/build-windows-installer.yml +4 -4
  7. .github/workflows/codeql-analysis.yml +3 -3
  8. .github/workflows/test-single.yml +4 -4
  9. .github/workflows/test.yml +9 -7
  10. .github/workflows/upload-pypi-source.yml +1 -1
  11. CHANGELOG.md +14 -0
  12. README.md +6 -6
  13. benchmarks/block_store.py +12 -12
  14. benchmarks/coin_store.py +2 -1
  15. benchmarks/streamable.py +2 -1
  16. benchmarks/utils.py +1 -153
  17. build_scripts/pyinstaller.spec +0 -33
  18. chia/cmds/check_wallet_db.py +1 -1
  19. chia/cmds/configure.py +4 -4
  20. chia/cmds/show_funcs.py +1 -1
  21. chia/cmds/wallet.py +14 -3
  22. chia/cmds/wallet_funcs.py +3 -1
  23. chia/consensus/block_body_validation.py +6 -4
  24. chia/consensus/block_creation.py +2 -7
  25. chia/consensus/block_header_validation.py +15 -22
  26. chia/consensus/full_block_to_block_record.py +6 -6
  27. chia/consensus/make_sub_epoch_summary.py +1 -1
  28. chia/consensus/multiprocess_validation.py +2 -2
  29. chia/daemon/server.py +3 -2
  30. chia/data_layer/data_layer_util.py +4 -0
  31. chia/data_layer/data_store.py +70 -41
  32. chia/data_layer/util/benchmark.py +1 -1
  33. chia/full_node/full_node.py +44 -25
  34. chia/full_node/full_node_api.py +11 -15
  35. chia/full_node/full_node_store.py +25 -21
  36. chia/full_node/hint_management.py +2 -2
  37. chia/full_node/subscriptions.py +157 -97
  38. chia/full_node/weight_proof.py +27 -27
  39. chia/plotting/cache.py +18 -1
  40. chia/pools/pool_puzzles.py +2 -2
  41. chia/rpc/full_node_rpc_api.py +15 -12
  42. chia/rpc/full_node_rpc_client.py +4 -0
  43. chia/rpc/wallet_rpc_api.py +1 -1
  44. chia/rpc/wallet_rpc_client.py +255 -296
  45. chia/simulator/block_tools.py +10 -10
  46. chia/timelord/iters_from_block.py +3 -3
  47. chia/timelord/timelord.py +13 -13
  48. chia/timelord/timelord_state.py +7 -7
  49. chia/types/blockchain_format/classgroup.py +2 -31
  50. chia/types/blockchain_format/foliage.py +5 -58
  51. chia/types/blockchain_format/pool_target.py +2 -11
  52. chia/types/blockchain_format/proof_of_space.py +4 -14
  53. chia/types/blockchain_format/reward_chain_block.py +3 -53
  54. chia/types/blockchain_format/serialized_program.py +2 -112
  55. chia/types/blockchain_format/slots.py +6 -51
  56. chia/types/blockchain_format/sub_epoch_summary.py +2 -15
  57. chia/types/blockchain_format/vdf.py +3 -18
  58. chia/types/end_of_slot_bundle.py +2 -18
  59. chia/types/full_block.py +3 -3
  60. chia/types/header_block.py +3 -3
  61. chia/types/transaction_queue_entry.py +3 -3
  62. chia/types/unfinished_block.py +1 -1
  63. chia/types/unfinished_header_block.py +1 -1
  64. chia/types/weight_proof.py +7 -61
  65. chia/util/full_block_utils.py +1 -3
  66. chia/util/initial-config.yaml +5 -0
  67. chia/util/keyring_wrapper.py +3 -3
  68. chia/util/misc.py +25 -0
  69. chia/util/profiler.py +16 -0
  70. chia/util/recursive_replace.py +12 -2
  71. chia/util/struct_stream.py +8 -1
  72. chia/wallet/block_record.py +0 -37
  73. chia/wallet/conditions.py +3 -3
  74. chia/wallet/did_wallet/did_wallet.py +15 -21
  75. chia/wallet/did_wallet/did_wallet_puzzles.py +13 -9
  76. chia/wallet/key_val_store.py +2 -2
  77. chia/wallet/lineage_proof.py +29 -1
  78. chia/wallet/puzzles/tails.py +1 -1
  79. chia/wallet/trade_manager.py +1 -1
  80. chia/wallet/util/merkle_utils.py +2 -0
  81. chia/wallet/util/peer_request_cache.py +1 -1
  82. chia/wallet/vc_wallet/cr_cat_drivers.py +9 -4
  83. chia/wallet/vc_wallet/vc_drivers.py +2 -8
  84. chia/wallet/wallet_blockchain.py +3 -3
  85. chia/wallet/wallet_node.py +1 -1
  86. install.sh +3 -1
  87. mypy-exclusions.txt +0 -12
  88. setup.py +14 -14
  89. tests/blockchain/test_blockchain.py +18 -24
  90. tests/blockchain/test_lookup_fork_chain.py +1 -1
  91. tests/build-job-matrix.py +2 -2
  92. tests/cmds/wallet/test_did.py +1 -1
  93. tests/cmds/wallet/test_wallet.py +3 -1
  94. tests/core/data_layer/test_data_rpc.py +36 -0
  95. tests/core/data_layer/test_data_store.py +37 -10
  96. tests/core/full_node/stores/test_coin_store.py +5 -5
  97. tests/core/full_node/stores/test_full_node_store.py +7 -5
  98. tests/core/full_node/test_full_node.py +3 -5
  99. tests/core/full_node/test_subscriptions.py +116 -70
  100. tests/core/mempool/test_mempool_performance.py +2 -7
  101. tests/core/server/test_loop.py +5 -0
  102. tests/core/test_coins.py +1 -1
  103. tests/core/test_full_node_rpc.py +4 -0
  104. tests/core/util/test_keychain.py +5 -3
  105. tests/core/util/test_streamable.py +1 -1
  106. tests/pools/test_pool_wallet.py +1 -1
  107. tests/tools/test_full_sync.py +1 -1
  108. tests/tools/test_legacy_keyring.py +1 -1
  109. tests/tools/test_run_block.py +1 -1
  110. tests/util/benchmark_cost.py +2 -2
  111. tests/util/benchmarks.py +155 -0
  112. tests/util/full_sync.py +226 -0
  113. tests/util/misc.py +7 -1
  114. tests/util/run_block.py +161 -0
  115. tests/util/test_full_block_utils.py +1 -1
  116. tests/util/test_misc.py +106 -1
  117. tests/util/test_network_protocol_test.py +26 -1
  118. tests/util/test_recursive_replace.py +116 -0
  119. tests/wallet/cat_wallet/test_cat_wallet.py +831 -906
  120. tests/wallet/cat_wallet/test_offer_lifecycle.py +151 -204
  121. tests/wallet/cat_wallet/test_trades.py +1 -1
  122. tests/wallet/dao_wallet/test_dao_clvm.py +2 -2
  123. tests/wallet/did_wallet/test_did.py +67 -4
  124. tests/wallet/nft_wallet/test_nft_bulk_mint.py +6 -0
  125. tests/wallet/nft_wallet/test_nft_offers.py +273 -0
  126. tests/wallet/nft_wallet/test_nft_wallet.py +170 -174
  127. tests/wallet/rpc/test_wallet_rpc.py +35 -20
  128. tests/wallet/simple_sync/test_simple_sync_protocol.py +622 -628
  129. tests/wallet/sync/test_wallet_sync.py +1087 -1143
  130. tests/wallet/test_bech32m.py +4 -4
  131. tests/wallet/test_puzzle_store.py +51 -52
  132. tests/wallet/test_singleton.py +15 -14
  133. tests/wallet/test_singleton_lifecycle.py +3 -4
  134. tests/wallet/test_singleton_lifecycle_fast.py +11 -10
  135. tests/wallet/test_util.py +87 -0
  136. tests/wallet/test_wallet_blockchain.py +95 -76
  137. tests/wallet/test_wallet_node.py +87 -88
  138. tests/wallet/test_wallet_user_store.py +2 -1
  139. tools/generate_chain.py +1 -1
  140. tools/run_block.py +1 -155
  141. tools/test_full_sync.py +2 -211
.github/actions/install/action.yml CHANGED
@@@ -63,7 -63,7 +63,7 @@@ runs
63
63

64
64
- name: Upload constraints file
65
65
if: inputs.constraints-file-artifact-name != ''
66
-- uses: actions/upload-artifact@v3
66
++ uses: actions/upload-artifact@v4
67
67
with:
68
68
name: ${{ inputs.constraints-file-artifact-name }}
69
69
path: venv/constraints.txt
.github/workflows/benchmarks.yml CHANGED
@@@ -115,7 -115,7 +115,7 @@@ jobs
115
115

116
116
- name: Publish JUnit results
117
117
if: always()
118
-- uses: actions/upload-artifact@v3
118
++ uses: actions/upload-artifact@v4
119
119
with:
120
120
name: junit-data
121
121
path: junit-data/*
.github/workflows/build-linux-installer-deb.yml CHANGED
@@@ -191,7 -191,7 +191,7 @@@ jobs
191
191
sh build_linux_deb-2-installer.sh ${{ matrix.os.arch }}
192
192

193
193
- name: Upload Linux artifacts
194
-- uses: actions/upload-artifact@v3
194
++ uses: actions/upload-artifact@v4
195
195
with:
196
196
name: chia-installers-linux-deb-${{ matrix.os.arch }}
197
197
path: ${{ github.workspace }}/build_scripts/final_installer/
@@@ -233,7 -233,7 +233,7 @@@
233
233
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
234
234

235
235
- name: Download constraints file
236
-- uses: actions/download-artifact@v3
236
++ uses: actions/download-artifact@v4
237
237
with:
238
238
name: constraints-file-${{ matrix.os.arch }}
239
239
path: venv
@@@ -243,7 -243,7 +243,7 @@@
243
243
pip install --constraint venv/constraints.txt py3createtorrent
244
244

245
245
- name: Download packages
246
-- uses: actions/download-artifact@v3
246
++ uses: actions/download-artifact@v4
247
247
with:
248
248
name: chia-installers-linux-deb-${{ matrix.os.arch }}
249
249
path: build_scripts/final_installer/
@@@ -396,7 -396,7 +396,7 @@@
396
396
- uses: Chia-Network/actions/clean-workspace@main
397
397

398
398
- name: Download packages
399
-- uses: actions/download-artifact@v3
399
++ uses: actions/download-artifact@v4
400
400
id: download
401
401
with:
402
402
name: chia-installers-linux-deb-${{ matrix.arch.artifact-name }}
.github/workflows/build-linux-installer-rpm.yml CHANGED
@@@ -184,7 -184,7 +184,7 @@@ jobs
184
184
bash build_linux_rpm-2-installer.sh amd64
185
185

186
186
- name: Upload fpm-generated rpm spec files
187
-- uses: actions/upload-artifact@v3
187
++ uses: actions/upload-artifact@v4
188
188
with:
189
189
if-no-files-found: error
190
190
name: spec
@@@ -193,7 -193,7 +193,7 @@@
193
193
build_scripts/dist/gui.spec
194
194

195
195
- name: Upload Linux artifacts
196
-- uses: actions/upload-artifact@v3
196
++ uses: actions/upload-artifact@v4
197
197
with:
198
198
name: chia-installers-linux-rpm-intel
199
199
path: ${{ github.workspace }}/build_scripts/final_installer/
@@@ -229,7 -229,7 +229,7 @@@
229
229
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
230
230

231
231
- name: Download constraints file
232
-- uses: actions/download-artifact@v3
232
++ uses: actions/download-artifact@v4
233
233
with:
234
234
name: constraints-file-intel
235
235
path: venv
@@@ -239,7 -239,7 +239,7 @@@
239
239
pip install --constraint venv/constraints.txt py3createtorrent
240
240

241
241
- name: Download packages
242
-- uses: actions/download-artifact@v3
242
++ uses: actions/download-artifact@v4
243
243
with:
244
244
name: chia-installers-linux-rpm-intel
245
245
path: build_scripts/final_installer/
@@@ -394,7 -394,7 +394,7 @@@
394
394
- uses: Chia-Network/actions/clean-workspace@main
395
395

396
396
- name: Download packages
397
-- uses: actions/download-artifact@v3
397
++ uses: actions/download-artifact@v4
398
398
id: download
399
399
with:
400
400
name: chia-installers-linux-rpm-intel
.github/workflows/build-macos-installers.yml CHANGED
@@@ -230,7 -230,7 +230,7 @@@ jobs
230
230
sh build_macos-2-installer.sh
231
231

232
232
- name: Upload MacOS artifacts
233
-- uses: actions/upload-artifact@v3
233
++ uses: actions/upload-artifact@v4
234
234
with:
235
235
name: chia-installers-macos-dmg-${{ matrix.os.name }}
236
236
path: ${{ github.workspace }}/build_scripts/final_installer/
@@@ -284,7 -284,7 +284,7 @@@
284
284
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
285
285

286
286
- name: Download constraints file
287
-- uses: actions/download-artifact@v3
287
++ uses: actions/download-artifact@v4
288
288
with:
289
289
name: constraints-file-${{ matrix.os.name }}
290
290
path: venv
@@@ -294,7 -294,7 +294,7 @@@
294
294
pip install --constraint venv/constraints.txt py3createtorrent
295
295

296
296
- name: Download packages
297
-- uses: actions/download-artifact@v3
297
++ uses: actions/download-artifact@v4
298
298
with:
299
299
name: chia-installers-macos-dmg-${{ matrix.os.name }}
300
300
path: build_scripts/final_installer/
@@@ -437,7 -437,7 +437,7 @@@
437
437
- uses: Chia-Network/actions/clean-workspace@main
438
438

439
439
- name: Download packages
440
-- uses: actions/download-artifact@v3
440
++ uses: actions/download-artifact@v4
441
441
id: download
442
442
with:
443
443
name: chia-installers-macos-dmg-${{ matrix.arch.artifact-name }}
.github/workflows/build-windows-installer.yml CHANGED
@@@ -258,7 -258,7 +258,7 @@@ jobs
258
258
.\build_windows-2-installer.ps1
259
259

260
260
- name: Upload Installer to artifacts
261
-- uses: actions/upload-artifact@v3
261
++ uses: actions/upload-artifact@v4
262
262
with:
263
263
name: chia-installers-windows-exe-intel
264
264
path: ${{ github.workspace }}\chia-blockchain-gui\release-builds\
@@@ -302,7 -302,7 +302,7 @@@
302
302
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
303
303

304
304
- name: Download constraints file
305
-- uses: actions/download-artifact@v3
305
++ uses: actions/download-artifact@v4
306
306
with:
307
307
name: constraints-file-intel
308
308
path: venv
@@@ -312,7 -312,7 +312,7 @@@
312
312
pip install --constraint venv/constraints.txt py3createtorrent
313
313

314
314
- name: Download packages
315
-- uses: actions/download-artifact@v3
315
++ uses: actions/download-artifact@v4
316
316
with:
317
317
name: chia-installers-windows-exe-intel
318
318
path: chia-blockchain-gui/release-builds/
@@@ -434,7 -434,7 +434,7 @@@
434
434
- uses: Chia-Network/actions/clean-workspace@main
435
435

436
436
- name: Download packages
437
-- uses: actions/download-artifact@v3
437
++ uses: actions/download-artifact@v4
438
438
with:
439
439
name: chia-installers-windows-exe-intel
440
440
path: packages
.github/workflows/codeql-analysis.yml CHANGED
@@@ -47,7 -47,7 +47,7 @@@ jobs
47
47

48
48
# Initializes the CodeQL tools for scanning.
49
49
- name: Initialize CodeQL
50
-- uses: github/codeql-action/init@v2
50
++ uses: github/codeql-action/init@v3
51
51
with:
52
52
languages: ${{ matrix.language }}
53
53
# If you wish to specify custom queries, you can do so here or in a config file.
@@@ -58,7 -58,7 +58,7 @@@
58
58
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
59
59
# If this step fails, then you should remove it and run the build manually (see below)
60
60
- name: Autobuild
61
-- uses: github/codeql-action/autobuild@v2
61
++ uses: github/codeql-action/autobuild@v3
62
62

63
63
# ℹ️ Command-line programs to run using the OS shell.
64
64
# 📚 https://git.io/JvXDl
@@@ -72,4 -72,4 +72,4 @@@
72
72
# make release
73
73

74
74
- name: Perform CodeQL Analysis
75
-- uses: github/codeql-action/analyze@v2
75
++ uses: github/codeql-action/analyze@v3
.github/workflows/test-single.yml CHANGED
@@@ -228,9 -228,9 +228,9 @@@ jobs
228
228
mv notchia/ chia/
229
229

230
230
- name: Publish JUnit results
231
-- uses: actions/upload-artifact@v3
231
++ uses: actions/upload-artifact@v4
232
232
with:
233
-- name: junit-data
233
++ name: junit-data-${{ env.JOB_FILE_NAME }}
234
234
path: junit-data/*
235
235
if-no-files-found: error
236
236

@@@ -243,9 -243,9 +243,9 @@@
243
243
coverage report --rcfile=.coveragerc --show-missing
244
244

245
245
- name: Publish coverage data
246
-- uses: actions/upload-artifact@v3
246
++ uses: actions/upload-artifact@v4
247
247
with:
248
-- name: coverage-data
248
++ name: coverage-data-${{ env.JOB_FILE_NAME }}
249
249
path: coverage-data/*
250
250
if-no-files-found: error
251
251

.github/workflows/test.yml CHANGED
@@@ -119,9 -119,9 +119,10 @@@ jobs
119
119
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
120
120

121
121
- name: Download Results
122
-- uses: actions/download-artifact@v3
122
++ uses: actions/download-artifact@v4
123
123
with:
124
-- name: junit-data
124
++ merge-multiple: true
125
++ pattern: junit-data-*
125
126
path: junit-data
126
127

127
128
- name: Format JUnit data and prepare results
@@@ -135,7 -135,7 +136,7 @@@
135
136
ls junit-results/*.xml | xargs --max-procs=10 --replace={} yq eval '.testsuites.testsuite |= sort_by(.+@name) | .testsuites.testsuite[].testcase |= sort_by(.+@classname, .+@name)' --inplace {}
136
137

137
138
- name: Publish formatted JUnit data
138
-- uses: actions/upload-artifact@v3
139
++ uses: actions/upload-artifact@v4
139
140
with:
140
141
name: junit-data
141
142
path: junit-data/*
@@@ -143,16 -143,16 +144,17 @@@
143
144

144
145
- name: Publish JUnit results
145
146
if: always()
146
-- uses: actions/upload-artifact@v3
147
++ uses: actions/upload-artifact@v4
147
148
with:
148
149
name: junit-results
149
150
path: junit-results/*
150
151
if-no-files-found: error
151
152

152
153
- name: Download Coverage
153
-- uses: actions/download-artifact@v3
154
++ uses: actions/download-artifact@v4
154
155
with:
155
-- name: coverage-data
156
++ merge-multiple: true
157
++ pattern: coverage-data-*
156
158
path: coverage-data
157
159

158
160
- name: Set up ${{ matrix.python.name }}
@@@ -280,7 -280,7 +282,7 @@@
280
282

281
283
- name: Publish coverage reports
282
284
if: always()
283
-- uses: actions/upload-artifact@v3
285
++ uses: actions/upload-artifact@v4
284
286
with:
285
287
name: coverage-reports
286
288
path: coverage-reports/*
.github/workflows/upload-pypi-source.yml CHANGED
@@@ -196,7 -196,7 +196,7 @@@ jobs
196
196
python -m build --sdist --outdir dist .
197
197

198
198
- name: Upload artifacts
199
-- uses: actions/upload-artifact@v3
199
++ uses: actions/upload-artifact@v4
200
200
with:
201
201
name: dist
202
202
path: ./dist
CHANGELOG.md CHANGED
@@@ -6,6 -6,6 +6,20 @@@ The format is based on [Keep a Changelo
6
6
and this project does not yet adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
7
7
for setuptools_scm/PEP 440 reasons.
8
8

9
++ ## 2.1.4 Chia blockchain 2024-01-10
10
++
11
++ ### Fixed
12
++ * Update chia_rs to 0.2.15 for AMD K10 architecture (fixes #16386)
13
++
14
++ ### Changed
15
++ * improved CPU usage due to tight loop in `send_transaction()`
16
++ * improve performance of `total_mempool_fees()` and `total_mempool_cost()`
17
++ * reduced the default maximum peer count to 40 from 80 (only applies to new configs)
18
++ * changed to `normal` SQlite db sync option (previously was `full`)
19
++ * reduced the mempool size to 10 blocks from 50 blocks (improves performance)
20
++ * improve performance of the mempool by batch fetching items from the db
21
++
22
++
9
23
## 2.1.3 Chia blockchain 2023-12-18
10
24

11
25
### Fixed
README.md CHANGED
@@@ -61,13 -61,13 +61,13 @@@ for consensus
61
61
## Installing
62
62

63
63
Install instructions are available in the
64
-- [INSTALL](https://github.com/Chia-Network/chia-blockchain/wiki/INSTALL)
64
++ [Installation Details](https://docs.chia.net/installation/)
65
65
section of the
66
-- [chia-blockchain repository wiki](https://github.com/Chia-Network/chia-blockchain/wiki).
66
++ [Chia Docs](https://docs.chia.net/introduction/).
67
67

68
68
## Running
69
69

70
-- Once installed, a
71
-- [Quick Start Guide](https://github.com/Chia-Network/chia-blockchain/wiki/Quick-Start-Guide)
72
-- is available from the repository
73
-- [wiki](https://github.com/Chia-Network/chia-blockchain/wiki).
70
++ Once installed, an
71
++ [Introduction to Chia](https://docs.chia.net/introduction/)
72
++ guide is available in the
73
++ [Chia Docs](https://docs.chia.net/introduction/).
benchmarks/block_store.py CHANGED
@@@ -8,18 -8,18 +8,7 @@@ from pathlib import Pat
8
8
from time import monotonic
9
9
from typing import List
10
10

11
-- from benchmarks.utils import (
12
-- clvm_generator,
13
-- rand_bytes,
14
-- rand_class_group_element,
15
-- rand_g1,
16
-- rand_g2,
17
-- rand_hash,
18
-- rand_vdf,
19
-- rand_vdf_proof,
20
-- rewards,
21
-- setup_db,
22
-- )
11
++ from benchmarks.utils import setup_db
23
12
from chia.consensus.block_record import BlockRecord
24
13
from chia.full_node.block_store import BlockStore
25
14
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
@@@ -31,6 -31,6 +20,17 @@@ from chia.types.blockchain_format.sized
31
20
from chia.types.blockchain_format.sub_epoch_summary import SubEpochSummary
32
21
from chia.types.full_block import FullBlock
33
22
from chia.util.ints import uint8, uint32, uint64, uint128
23
++ from tests.util.benchmarks import (
24
++ clvm_generator,
25
++ rand_bytes,
26
++ rand_class_group_element,
27
++ rand_g1,
28
++ rand_g2,
29
++ rand_hash,
30
++ rand_vdf,
31
++ rand_vdf_proof,
32
++ rewards,
33
++ )
34
34

35
35
# to run this benchmark:
36
36
# python -m benchmarks.coin_store
benchmarks/coin_store.py CHANGED
@@@ -8,11 -8,11 +8,12 @@@ from pathlib import Pat
8
8
from time import monotonic
9
9
from typing import List, Tuple
10
10

11
-- from benchmarks.utils import rand_hash, rewards, setup_db
11
++ from benchmarks.utils import setup_db
12
12
from chia.full_node.coin_store import CoinStore
13
13
from chia.types.blockchain_format.coin import Coin
14
14
from chia.types.blockchain_format.sized_bytes import bytes32
15
15
from chia.util.ints import uint32, uint64
16
++ from tests.util.benchmarks import rand_hash, rewards
16
17

17
18
# to run this benchmark:
18
19
# python -m benchmarks.coin_store
benchmarks/streamable.py CHANGED
@@@ -10,11 -10,11 +10,12 @@@ from typing import Any, Callable, Dict
10
10

11
11
import click
12
12

13
-- from benchmarks.utils import EnumType, get_commit_hash, rand_bytes, rand_full_block, rand_hash
13
++ from benchmarks.utils import EnumType, get_commit_hash
14
14
from chia.types.blockchain_format.sized_bytes import bytes32
15
15
from chia.types.full_block import FullBlock
16
16
from chia.util.ints import uint8, uint64
17
17
from chia.util.streamable import Streamable, streamable
18
++ from tests.util.benchmarks import rand_bytes, rand_full_block, rand_hash
18
19

19
20
# to run this benchmark:
20
21
# python -m benchmarks.streamable
benchmarks/utils.py CHANGED
@@@ -3,36 -3,36 +3,14 @@@ from __future__ import annotation
3
3
import contextlib
4
4
import enum
5
5
import os
6
-- import random
7
6
import subprocess
8
7
import sys
9
8
from pathlib import Path
10
-- from typing import Any, AsyncIterator, Generic, Optional, Tuple, Type, TypeVar, Union
9
++ from typing import Any, AsyncIterator, Generic, Optional, Type, TypeVar, Union
11
10

12
11
import click
13
-- from chia_rs import AugSchemeMPL, G1Element, G2Element
14
12

15
-- from chia.consensus.coinbase import create_farmer_coin, create_pool_coin
16
-- from chia.consensus.default_constants import DEFAULT_CONSTANTS
17
-- from chia.types.blockchain_format.classgroup import ClassgroupElement
18
-- from chia.types.blockchain_format.coin import Coin
19
-- from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
20
-- from chia.types.blockchain_format.pool_target import PoolTarget
21
-- from chia.types.blockchain_format.proof_of_space import ProofOfSpace
22
-- from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
23
-- from chia.types.blockchain_format.serialized_program import SerializedProgram
24
-- from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
25
-- from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
26
-- from chia.types.full_block import FullBlock
27
13
from chia.util.db_wrapper import DBWrapper2
28
-- from chia.util.ints import uint8, uint32, uint64, uint128
29
--
30
-- # farmer puzzle hash
31
-- ph = bytes32(b"a" * 32)
32
--
33
-- with open(Path(os.path.realpath(__file__)).parent / "clvm_generator.bin", "rb") as f:
34
-- clvm_generator = f.read()
35
--
36
14

37
15
_T_Enum = TypeVar("_T_Enum", bound=enum.Enum)
38
16

@@@ -48,136 -48,136 +26,6 @@@ class EnumType(click.Choice, Generic[_T
48
26
return self.__enum(converted_str)
49
27

50
28

51
-- def rewards(height: uint32) -> Tuple[Coin, Coin]:
52
-- farmer_coin = create_farmer_coin(height, ph, uint64(250000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
53
-- pool_coin = create_pool_coin(height, ph, uint64(1750000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
54
-- return farmer_coin, pool_coin
55
--
56
--
57
-- def rand_bytes(num: int) -> bytes:
58
-- ret = bytearray(num)
59
-- for i in range(num):
60
-- ret[i] = random.getrandbits(8)
61
-- return bytes(ret)
62
--
63
--
64
-- def rand_hash() -> bytes32:
65
-- return bytes32(rand_bytes(32))
66
--
67
--
68
-- def rand_g1() -> G1Element:
69
-- sk = AugSchemeMPL.key_gen(rand_bytes(96))
70
-- return sk.get_g1()
71
--
72
--
73
-- def rand_g2() -> G2Element:
74
-- sk = AugSchemeMPL.key_gen(rand_bytes(96))
75
-- return AugSchemeMPL.sign(sk, b"foobar")
76
--
77
--
78
-- def rand_class_group_element() -> ClassgroupElement:
79
-- return ClassgroupElement(bytes100(rand_bytes(100)))
80
--
81
--
82
-- def rand_vdf() -> VDFInfo:
83
-- return VDFInfo(rand_hash(), uint64(random.randint(100000, 1000000000)), rand_class_group_element())
84
--
85
--
86
-- def rand_vdf_proof() -> VDFProof:
87
-- return VDFProof(
88
-- uint8(1), # witness_type
89
-- rand_hash(), # witness
90
-- bool(random.randint(0, 1)), # normalized_to_identity
91
-- )
92
--
93
--
94
-- def rand_full_block() -> FullBlock:
95
-- proof_of_space = ProofOfSpace(
96
-- rand_hash(),
97
-- rand_g1(),
98
-- None,
99
-- rand_g1(),
100
-- uint8(0),
101
-- rand_bytes(8 * 32),
102
-- )
103
--
104
-- reward_chain_block = RewardChainBlock(
105
-- uint128(1),
106
-- uint32(2),
107
-- uint128(3),
108
-- uint8(4),
109
-- rand_hash(),
110
-- proof_of_space,
111
-- None,
112
-- rand_g2(),
113
-- rand_vdf(),
114
-- None,
115
-- rand_g2(),
116
-- rand_vdf(),
117
-- rand_vdf(),
118
-- True,
119
-- )
120
--
121
-- pool_target = PoolTarget(
122
-- rand_hash(),
123
-- uint32(0),
124
-- )
125
--
126
-- foliage_block_data = FoliageBlockData(
127
-- rand_hash(),
128
-- pool_target,
129
-- rand_g2(),
130
-- rand_hash(),
131
-- rand_hash(),
132
-- )
133
--
134
-- foliage = Foliage(
135
-- rand_hash(),
136
-- rand_hash(),
137
-- foliage_block_data,
138
-- rand_g2(),
139
-- rand_hash(),
140
-- rand_g2(),
141
-- )
142
--
143
-- foliage_transaction_block = FoliageTransactionBlock(
144
-- rand_hash(),
145
-- uint64(0),
146
-- rand_hash(),
147
-- rand_hash(),
148
-- rand_hash(),
149
-- rand_hash(),
150
-- )
151
--
152
-- farmer_coin, pool_coin = rewards(uint32(0))
153
--
154
-- transactions_info = TransactionsInfo(
155
-- rand_hash(),
156
-- rand_hash(),
157
-- rand_g2(),
158
-- uint64(0),
159
-- uint64(1),
160
-- [farmer_coin, pool_coin],
161
-- )
162
--
163
-- full_block = FullBlock(
164
-- [],
165
-- reward_chain_block,
166
-- rand_vdf_proof(),
167
-- rand_vdf_proof(),
168
-- rand_vdf_proof(),
169
-- rand_vdf_proof(),
170
-- rand_vdf_proof(),
171
-- foliage,
172
-- foliage_transaction_block,
173
-- transactions_info,
174
-- SerializedProgram.from_bytes(clvm_generator),
175
-- [],
176
-- )
177
--
178
-- return full_block
179
--
180
--
181
29
@contextlib.asynccontextmanager
182
30
async def setup_db(name: Union[str, os.PathLike[str]], db_version: int) -> AsyncIterator[DBWrapper2]:
183
31
db_filename = Path(name)
build_scripts/pyinstaller.spec CHANGED
@@@ -13,37 -13,37 +13,6 @@@ THIS_IS_MAC = platform.system().lower()
13
13
ROOT = pathlib.Path(importlib.import_module("chia").__file__).absolute().parent.parent
14
14

15
15

16
-- def solve_name_collision_problem(analysis):
17
-- """
18
-- There is a collision between the `chia` file name (which is the executable)
19
-- and the `chia` directory, which contains non-code resources like `english.txt`.
20
-- We move all the resources in the zipped area so there is no
21
-- need to create the `chia` directory, since the names collide.
22
--
23
-- Fetching data now requires going into a zip file, so it will be slower.
24
-- It's best if files that are used frequently are cached.
25
--
26
-- A sample large compressible file (1 MB of `/dev/zero`), seems to be
27
-- about eight times slower.
28
--
29
-- Note that this hack isn't documented, but seems to work.
30
-- """
31
--
32
-- zipped = []
33
-- datas = []
34
-- for data in analysis.datas:
35
-- if str(data[0]).startswith("chia/"):
36
-- zipped.append(data)
37
-- else:
38
-- datas.append(data)
39
--
40
-- # items in this field are included in the binary
41
-- analysis.zipped_data = zipped
42
--
43
-- # these items will be dropped in the root folder uncompressed
44
-- analysis.datas = datas
45
--
46
--
47
16
keyring_imports = collect_submodules("keyring.backends")
48
17

49
18
# keyring uses entrypoints to read keyring.backends from metadata file entry_points.txt.
@@@ -176,8 -176,8 +145,6 @@@ def add_binary(name, path_to_script, co
176
145
noarchive=False,
177
146
)
178
147

179
-- solve_name_collision_problem(analysis)
180
--
181
148
binary_pyz = PYZ(analysis.pure, analysis.zipped_data, cipher=block_cipher)
182
149

183
150
binary_exe = EXE(
chia/cmds/check_wallet_db.py CHANGED
@@@ -206,7 -206,7 +206,7 @@@ def print_min_max_derivation_for_wallet
206
206
class WalletDBReader:
207
207
db_wrapper: DBWrapper2 # TODO: Remove db_wrapper member
208
208
config = {"db_readers": 1}
209
-- sql_log_path = None
209
++ sql_log_path: Optional[Path] = None
210
210
verbose = False
211
211

212
212
async def get_all_wallets(self) -> List[Wallet]:
chia/cmds/configure.py CHANGED
@@@ -104,10 -104,10 +104,10 @@@ def configure
104
104
if testnet == "true" or testnet == "t":
105
105
print("Setting Testnet")
106
106
testnet_port = "58444"
107
-- testnet_introducer = "introducer-testnet10.chia.net"
108
-- testnet_dns_introducer = "dns-introducer-testnet10.chia.net"
109
-- bootstrap_peers = ["testnet10-node.chia.net"]
110
-- testnet = "testnet10"
107
++ testnet_introducer = "introducer-testnet11.chia.net"
108
++ testnet_dns_introducer = "dns-introducer-testnet11.chia.net"
109
++ bootstrap_peers = ["testnet11-node-us-west-2.chia.net"]
110
++ testnet = "testnet11"
111
111
config["full_node"]["port"] = int(testnet_port)
112
112
if config["full_node"]["introducer_peer"] is None:
113
113
config["full_node"]["introducer_peer"] = {}
chia/cmds/show_funcs.py CHANGED
@@@ -121,7 -121,7 +121,7 @@@ async def print_block_from_hash
121
121
cost = str(full_block.transactions_info.cost)
122
122
tx_filter_hash: Union[str, bytes32] = "Not a transaction block"
123
123
if full_block.foliage_transaction_block:
124
-- tx_filter_hash = full_block.foliage_transaction_block.filter_hash
124
++ tx_filter_hash = bytes32(full_block.foliage_transaction_block.filter_hash)
125
125
fees: Any = block.fees
126
126
else:
127
127
block_time_string = "Not a transaction block"
chia/cmds/wallet.py CHANGED
@@@ -441,7 -441,7 +441,6 @@@ def add_token_cmd(wallet_rpc_port: Opti
441
441
"-r",
442
442
"--request",
443
443
help="A wallet id of an asset to receive and the amount you wish to receive (formatted like wallet_id:amount)",
444
-- required=True,
445
444
multiple=True,
446
445
)
447
446
@click.option("-p", "--filepath", help="The path to write the generated offer file to", required=True)
@@@ -454,6 -454,6 +453,7 @@@
454
453
is_flag=True,
455
454
default=False,
456
455
)
456
++ @click.option("-o", "--override", help="Creates offer without checking for unusual values", is_flag=True, default=False)
457
457
def make_offer_cmd(
458
458
wallet_rpc_port: Optional[int],
459
459
fingerprint: int,
@@@ -462,9 -462,9 +462,14 @@@
462
462
filepath: str,
463
463
fee: str,
464
464
reuse: bool,
465
++ override: bool,
465
466
) -> None:
466
467
from .wallet_funcs import make_offer
467
468

469
++ if len(request) == 0 and not override:
470
++ print("Cannot make an offer without requesting something without --override")
471
++ return
472
++
468
473
asyncio.run(
469
474
make_offer(
470
475
wallet_rpc_port=wallet_rpc_port,
@@@ -873,14 -873,14 +878,20 @@@ def did_transfer_did
873
878
id: int,
874
879
target_address: str,
875
880
reset_recovery: bool,
876
-- fee: int,
881
++ fee: str,
877
882
reuse: bool,
878
883
) -> None:
879
884
from .wallet_funcs import transfer_did
880
885

881
886
asyncio.run(
882
887
transfer_did(
883
-- wallet_rpc_port, fingerprint, id, fee, target_address, reset_recovery is False, True if reuse else None
888
++ wallet_rpc_port,
889
++ fingerprint,
890
++ id,
891
++ Decimal(fee),
892
++ target_address,
893
++ reset_recovery is False,
894
++ True if reuse else None,
884
895
)
885
896
)
886
897

chia/cmds/wallet_funcs.py CHANGED
@@@ -1004,11 -1004,11 +1004,13 @@@ async def transfer_did
1004
1004
wallet_rpc_port: Optional[int],
1005
1005
fp: Optional[int],
1006
1006
did_wallet_id: int,
1007
-- fee: int,
1007
++ d_fee: Decimal,
1008
1008
target_address: str,
1009
1009
with_recovery: bool,
1010
1010
reuse_puzhash: Optional[bool],
1011
1011
) -> None:
1012
++ fee: int = int(d_fee * units["chia"])
1013
++
1012
1014
async with get_wallet_client(wallet_rpc_port, fp) as (wallet_client, fingerprint, config):
1013
1015
try:
1014
1016
response = await wallet_client.did_transfer_did(
chia/consensus/block_body_validation.py CHANGED
@@@ -109,12 -109,12 +109,14 @@@ class ForkInfo
109
109
self.removals_since_fork[bytes32(spend.coin_id)] = ForkRem(bytes32(spend.puzzle_hash), height)
110
110
for puzzle_hash, amount, hint in spend.create_coin:
111
111
coin = Coin(bytes32(spend.coin_id), bytes32(puzzle_hash), uint64(amount))
112
-- self.additions_since_fork[coin.name()] = ForkAdd(coin, height, timestamp, hint, False)
112
++ self.additions_since_fork[coin.name()] = ForkAdd(
113
++ coin, uint32(height), uint64(timestamp), hint, False
114
++ )
113
115
for coin in block.get_included_reward_coins():
114
116
assert block.foliage_transaction_block is not None
115
117
timestamp = block.foliage_transaction_block.timestamp
116
118
assert coin.name() not in self.additions_since_fork
117
-- self.additions_since_fork[coin.name()] = ForkAdd(coin, block.height, timestamp, None, True)
119
++ self.additions_since_fork[coin.name()] = ForkAdd(coin, uint32(block.height), uint64(timestamp), None, True)
118
120

119
121

120
122
async def validate_block_body(
@@@ -391,7 -391,7 +393,7 @@@
391
393
height,
392
394
height,
393
395
False,
394
-- block.foliage_transaction_block.timestamp,
396
++ uint64(block.foliage_transaction_block.timestamp),
395
397
)
396
398
removal_coin_records[new_unspent.name] = new_unspent
397
399
else:
@@@ -490,7 -490,7 +492,7 @@@
490
492
block_timestamp: uint64
491
493
if height < constants.SOFT_FORK2_HEIGHT:
492
494
# this does not happen on mainnet. testnet10 only
493
-- block_timestamp = block.foliage_transaction_block.timestamp # pragma: no cover
495
++ block_timestamp = uint64(block.foliage_transaction_block.timestamp) # pragma: no cover
494
496
else:
495
497
block_timestamp = prev_transaction_block_timestamp
496
498

chia/consensus/block_creation.py CHANGED
@@@ -2,7 -2,7 +2,6 @@@ from __future__ import annotation
2
2

3
3
import logging
4
4
import random
5
-- from dataclasses import replace
6
5
from typing import Callable, Dict, List, Optional, Sequence, Tuple
7
6

8
7
import chia_rs
@@@ -501,10 -501,10 +500,7 @@@ def unfinished_block_to_full_block
501
500
is_transaction_block,
502
501
)
503
502
if prev_block is None:
504
-- new_foliage = replace(
505
-- unfinished_block.foliage,
506
-- reward_block_hash=reward_chain_block.get_hash(),
507
-- )
503
++ new_foliage = unfinished_block.foliage.replace(reward_block_hash=reward_chain_block.get_hash())
508
504
else:
509
505
if is_transaction_block:
510
506
new_fbh = unfinished_block.foliage.foliage_transaction_block_hash
@@@ -513,8 -513,8 +509,7 @@@
513
509
new_fbh = None
514
510
new_fbs = None
515
511
assert (new_fbh is None) == (new_fbs is None)
516
-- new_foliage = replace(
517
-- unfinished_block.foliage,
512
++ new_foliage = unfinished_block.foliage.replace(
518
513
reward_block_hash=reward_chain_block.get_hash(),
519
514
prev_block_hash=prev_block.header_hash,
520
515
foliage_transaction_block_hash=new_fbh,
chia/consensus/block_header_validation.py CHANGED
@@@ -1,6 -1,6 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- import dataclasses
4
3
import logging
5
4
import time
6
5
from typing import Optional, Tuple
@@@ -67,7 -67,7 +66,7 @@@ def validate_unfinished_header_block
67
66
if genesis_block and header_block.prev_header_hash != constants.GENESIS_CHALLENGE:
68
67
return None, ValidationError(Err.INVALID_PREV_BLOCK_HASH)
69
68

70
-- overflow = is_overflow_block(constants, header_block.reward_chain_block.signage_point_index)
69
++ overflow = is_overflow_block(constants, uint8(header_block.reward_chain_block.signage_point_index))
71
70
if skip_overflow_last_ss_validation and overflow:
72
71
if final_eos_is_already_included(header_block, blocks, expected_sub_slot_iters):
73
72
skip_overflow_last_ss_validation = False
@@@ -194,9 -194,9 +193,9 @@@
194
193
icc_iters_proof,
195
194
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
196
195
)
197
-- if sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf != dataclasses.replace(
198
-- target_vdf_info,
199
-- number_of_iterations=icc_iters_committed,
196
++ if (
197
++ sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf
198
++ != target_vdf_info.replace(number_of_iterations=icc_iters_committed)
200
199
):
201
200
return None, ValidationError(Err.INVALID_ICC_EOS_VDF)
202
201
if not skip_vdf_is_valid:
@@@ -338,9 -338,9 +337,8 @@@
338
337
else:
339
338
cc_eos_vdf_info_iters = expected_sub_slot_iters
340
339
# Check that the modified data is correct
341
-- if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf != dataclasses.replace(
342
-- partial_cc_vdf_info,
343
-- number_of_iterations=cc_eos_vdf_info_iters,
340
++ if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf != partial_cc_vdf_info.replace(
341
++ number_of_iterations=cc_eos_vdf_info_iters
344
342
):
345
343
return None, ValidationError(Err.INVALID_CC_EOS_VDF, "wrong challenge chain end of slot vdf")
346
344

@@@ -526,13 -526,13 +524,13 @@@
526
524
sp_iters: uint64 = calculate_sp_iters(
527
525
constants,
528
526
expected_sub_slot_iters,
529
-- header_block.reward_chain_block.signage_point_index,
527
++ uint8(header_block.reward_chain_block.signage_point_index),
530
528
)
531
529

532
530
ip_iters: uint64 = calculate_ip_iters(
533
531
constants,
534
532
expected_sub_slot_iters,
535
-- header_block.reward_chain_block.signage_point_index,
533
++ uint8(header_block.reward_chain_block.signage_point_index),
536
534
required_iters,
537
535
)
538
536
if header_block.reward_chain_block.challenge_chain_sp_vdf is None:
@@@ -659,9 -659,9 +657,8 @@@
659
657
header_block.reward_chain_block.challenge_chain_sp_vdf.output,
660
658
)
661
659

662
-- if header_block.reward_chain_block.challenge_chain_sp_vdf != dataclasses.replace(
663
-- target_vdf_info,
664
-- number_of_iterations=sp_iters,
660
++ if header_block.reward_chain_block.challenge_chain_sp_vdf != target_vdf_info.replace(
661
++ number_of_iterations=sp_iters
665
662
):
666
663
return None, ValidationError(Err.INVALID_CC_SP_VDF)
667
664
if not skip_vdf_is_valid:
@@@ -879,7 -879,7 +876,7 @@@ def validate_finished_header_block
879
876
ip_iters: uint64 = calculate_ip_iters(
880
877
constants,
881
878
expected_sub_slot_iters,
882
-- header_block.reward_chain_block.signage_point_index,
879
++ uint8(header_block.reward_chain_block.signage_point_index),
883
880
required_iters,
884
881
)
885
882
if not genesis_block:
@@@ -944,14 -944,14 +941,10 @@@
944
941
ip_vdf_iters,
945
942
header_block.reward_chain_block.challenge_chain_ip_vdf.output,
946
943
)
947
-- if header_block.reward_chain_block.challenge_chain_ip_vdf != dataclasses.replace(
948
-- cc_target_vdf_info,
949
-- number_of_iterations=ip_iters,
944
++ if header_block.reward_chain_block.challenge_chain_ip_vdf != cc_target_vdf_info.replace(
945
++ number_of_iterations=ip_iters
950
946
):
951
-- expected = dataclasses.replace(
952
-- cc_target_vdf_info,
953
-- number_of_iterations=ip_iters,
954
-- )
947
++ expected = cc_target_vdf_info.replace(number_of_iterations=ip_iters)
955
948
log.error(f"{header_block.reward_chain_block.challenge_chain_ip_vdf }. expected {expected}")
956
949
log.error(f"Block: {header_block}")
957
950
return None, ValidationError(Err.INVALID_CC_IP_VDF)
@@@ -990,7 -990,7 +983,7 @@@
990
983

991
984
# 31. Check infused challenge chain infusion point VDF
992
985
if not genesis_block:
993
-- overflow = is_overflow_block(constants, header_block.reward_chain_block.signage_point_index)
986
++ overflow = is_overflow_block(constants, uint8(header_block.reward_chain_block.signage_point_index))
994
987
deficit = calculate_deficit(
995
988
constants,
996
989
header_block.height,
chia/consensus/full_block_to_block_record.py CHANGED
@@@ -39,7 -39,7 +39,7 @@@ def block_to_block_record
39
39
sub_slot_iters, _ = get_next_sub_slot_iters_and_difficulty(
40
40
constants, len(block.finished_sub_slots) > 0, prev_b, blocks
41
41
)
42
-- overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
42
++ overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
43
43
deficit = calculate_deficit(
44
44
constants,
45
45
block.height,
@@@ -62,8 -62,8 +62,8 @@@
62
62
blocks,
63
63
block.height,
64
64
blocks.block_record(prev_b.prev_hash),
65
-- block.finished_sub_slots[0].challenge_chain.new_difficulty,
66
-- block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters,
65
++ uint64.construct_optional(block.finished_sub_slots[0].challenge_chain.new_difficulty),
66
++ uint64.construct_optional(block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters),
67
67
)
68
68
if ses.get_hash() != found_ses_hash:
69
69
raise ValueError(Err.INVALID_SUB_EPOCH_SUMMARY)
@@@ -148,7 -148,7 +148,7 @@@ def header_block_to_sub_block_record
148
148
block.height,
149
149
block.weight,
150
150
block.total_iters,
151
-- block.reward_chain_block.signage_point_index,
151
++ uint8(block.reward_chain_block.signage_point_index),
152
152
block.reward_chain_block.challenge_chain_ip_vdf.output,
153
153
icc_output,
154
154
block.reward_chain_block.get_hash(),
@@@ -160,9 -160,9 +160,9 @@@
160
160
deficit,
161
161
overflow,
162
162
prev_transaction_block_height,
163
-- timestamp,
163
++ uint64.construct_optional(timestamp),
164
164
prev_transaction_block_hash,
165
-- fees,
165
++ uint64.construct_optional(fees),
166
166
reward_claims_incorporated,
167
167
finished_challenge_slot_hashes,
168
168
finished_infused_challenge_slot_hashes,
chia/consensus/make_sub_epoch_summary.py CHANGED
@@@ -94,7 -94,7 +94,7 @@@ def next_sub_epoch_summary
94
94
Returns:
95
95
object: the new sub-epoch summary
96
96
"""
97
-- signage_point_index = block.reward_chain_block.signage_point_index
97
++ signage_point_index = uint8(block.reward_chain_block.signage_point_index)
98
98
prev_b: Optional[BlockRecord] = blocks.try_block_record(block.prev_header_hash)
99
99
if prev_b is None or prev_b.height == 0:
100
100
return None
chia/consensus/multiprocess_validation.py CHANGED
@@@ -32,7 -32,7 +32,7 @@@ from chia.util.block_cache import Block
32
32
from chia.util.condition_tools import pkm_pairs
33
33
from chia.util.errors import Err, ValidationError
34
34
from chia.util.generator_tools import get_block_header, tx_removals_and_additions
35
-- from chia.util.ints import uint16, uint32, uint64
35
++ from chia.util.ints import uint8, uint16, uint32, uint64
36
36
from chia.util.streamable import Streamable, streamable
37
37

38
38
log = logging.getLogger(__name__)
@@@ -257,7 -257,7 +257,7 @@@ async def pre_validate_blocks_multiproc
257
257
constants, len(block.finished_sub_slots) > 0, prev_b, block_records
258
258
)
259
259

260
-- overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
260
++ overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
261
261
challenge = get_block_challenge(constants, block, BlockCache(recent_blocks), prev_b is None, overflow, False)
262
262
if block.reward_chain_block.challenge_chain_sp_vdf is None:
263
263
cc_sp_hash: bytes32 = challenge
chia/daemon/server.py CHANGED
@@@ -31,7 -31,7 +31,6 @@@ from chia.plotters.plotters import get_
31
31
from chia.plotting.util import add_plot_directory
32
32
from chia.server.server import ssl_context_for_server
33
33
from chia.util.bech32m import encode_puzzle_hash
34
-- from chia.util.beta_metrics import BetaMetricsLogger
35
34
from chia.util.chia_logging import initialize_service_logging
36
35
from chia.util.config import load_config
37
36
from chia.util.errors import KeychainCurrentPassphraseIsInvalid
@@@ -1534,8 -1534,8 +1533,10 @@@ async def async_run_daemon(root_path: P
1534
1533
with Lockfile.create(daemon_launch_lock_path(root_path), timeout=1):
1535
1534
log.info(f"chia-blockchain version: {chia_full_version_str()}")
1536
1535

1537
-- beta_metrics: Optional[BetaMetricsLogger] = None
1536
++ beta_metrics = None
1538
1537
if config.get("beta", {}).get("enabled", False):
1538
++ from chia.util.beta_metrics import BetaMetricsLogger
1539
++
1539
1540
beta_metrics = BetaMetricsLogger(root_path)
1540
1541
beta_metrics.start_logging()
1541
1542

chia/data_layer/data_layer_util.py CHANGED
@@@ -43,6 -43,6 +43,10 @@@ def leaf_hash(key: bytes, value: bytes
43
43
return Program.to((key, value)).get_tree_hash() # type: ignore[no-any-return]
44
44

45
45

46
++ def key_hash(key: bytes) -> bytes32:
47
++ return Program.to(key).get_tree_hash() # type: ignore[no-any-return]
48
++
49
++
46
50
async def _debug_dump(db: DBWrapper2, description: str = "") -> None:
47
51
async with db.reader() as reader:
48
52
cursor = await reader.execute("SELECT name FROM sqlite_master WHERE type='table';")
chia/data_layer/data_store.py CHANGED
@@@ -28,6 -28,6 +28,7 @@@ from chia.data_layer.data_layer_util im
28
28
Subscription,
29
29
TerminalNode,
30
30
internal_hash,
31
++ key_hash,
31
32
leaf_hash,
32
33
row_to_node,
33
34
)
@@@ -672,35 -672,35 +673,40 @@@ class DataStore
672
673

673
674
return internal_nodes
674
675

676
++ async def get_keys_values_cursor(
677
++ self, reader: aiosqlite.Connection, root_hash: Optional[bytes32]
678
++ ) -> aiosqlite.Cursor:
679
++ return await reader.execute(
680
++ """
681
++ WITH RECURSIVE
682
++ tree_from_root_hash(hash, node_type, left, right, key, value, depth, rights) AS (
683
++ SELECT node.*, 0 AS depth, 0 AS rights FROM node WHERE node.hash == :root_hash
684
++ UNION ALL
685
++ SELECT
686
++ node.*,
687
++ tree_from_root_hash.depth + 1 AS depth,
688
++ CASE
689
++ WHEN node.hash == tree_from_root_hash.right
690
++ THEN tree_from_root_hash.rights + (1 << (62 - tree_from_root_hash.depth))
691
++ ELSE tree_from_root_hash.rights
692
++ END AS rights
693
++ FROM node, tree_from_root_hash
694
++ WHERE node.hash == tree_from_root_hash.left OR node.hash == tree_from_root_hash.right
695
++ )
696
++ SELECT * FROM tree_from_root_hash
697
++ WHERE node_type == :node_type
698
++ ORDER BY depth ASC, rights ASC
699
++ """,
700
++ {"root_hash": root_hash, "node_type": NodeType.TERMINAL},
701
++ )
702
++
675
703
async def get_keys_values(self, tree_id: bytes32, root_hash: Optional[bytes32] = None) -> List[TerminalNode]:
676
704
async with self.db_wrapper.reader() as reader:
677
705
if root_hash is None:
678
706
root = await self.get_tree_root(tree_id=tree_id)
679
707
root_hash = root.node_hash
680
-- cursor = await reader.execute(
681
-- """
682
-- WITH RECURSIVE
683
-- tree_from_root_hash(hash, node_type, left, right, key, value, depth, rights) AS (
684
-- SELECT node.*, 0 AS depth, 0 AS rights FROM node WHERE node.hash == :root_hash
685
-- UNION ALL
686
-- SELECT
687
-- node.*,
688
-- tree_from_root_hash.depth + 1 AS depth,
689
-- CASE
690
-- WHEN node.hash == tree_from_root_hash.right
691
-- THEN tree_from_root_hash.rights + (1 << (62 - tree_from_root_hash.depth))
692
-- ELSE tree_from_root_hash.rights
693
-- END AS rights
694
-- FROM node, tree_from_root_hash
695
-- WHERE node.hash == tree_from_root_hash.left OR node.hash == tree_from_root_hash.right
696
-- )
697
-- SELECT * FROM tree_from_root_hash
698
-- WHERE node_type == :node_type
699
-- ORDER BY depth ASC, rights ASC
700
-- """,
701
-- {"root_hash": None if root_hash is None else root_hash, "node_type": NodeType.TERMINAL},
702
-- )
703
708

709
++ cursor = await self.get_keys_values_cursor(reader, root_hash)
704
710
terminal_nodes: List[TerminalNode] = []
705
711
async for row in cursor:
706
712
if row["depth"] > 62:
@@@ -722,6 -722,6 +728,26 @@@
722
728

723
729
return terminal_nodes
724
730

731
++ async def get_keys_values_compressed(
732
++ self, tree_id: bytes32, root_hash: Optional[bytes32] = None
733
++ ) -> Dict[bytes32, bytes32]:
734
++ async with self.db_wrapper.reader() as reader:
735
++ if root_hash is None:
736
++ root = await self.get_tree_root(tree_id=tree_id)
737
++ root_hash = root.node_hash
738
++
739
++ cursor = await self.get_keys_values_cursor(reader, root_hash)
740
++ kv_compressed: Dict[bytes32, bytes32] = {}
741
++ async for row in cursor:
742
++ if row["depth"] > 62:
743
++ raise Exception("Tree depth exceeded 62, unable to guarantee left-to-right node order.")
744
++ node = row_to_node(row=row)
745
++ if not isinstance(node, TerminalNode):
746
++ raise Exception(f"Unexpected internal node found: {node.hash.hex()}")
747
++ kv_compressed[key_hash(node.key)] = leaf_hash(node.key, node.value)
748
++
749
++ return kv_compressed
750
++
725
751
async def get_node_type(self, node_hash: bytes32) -> NodeType:
726
752
async with self.db_wrapper.reader() as reader:
727
753
cursor = await reader.execute(
@@@ -795,7 -795,7 +821,7 @@@
795
821
key: bytes,
796
822
value: bytes,
797
823
tree_id: bytes32,
798
-- hint_keys_values: Optional[Dict[bytes, bytes]] = None,
824
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
799
825
use_optimized: bool = True,
800
826
status: Status = Status.PENDING,
801
827
root: Optional[Root] = None,
@@@ -941,7 -941,7 +967,7 @@@
941
967
tree_id: bytes32,
942
968
reference_node_hash: Optional[bytes32],
943
969
side: Optional[Side],
944
-- hint_keys_values: Optional[Dict[bytes, bytes]] = None,
970
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
945
971
use_optimized: bool = True,
946
972
status: Status = Status.PENDING,
947
973
root: Optional[Root] = None,
@@@ -959,7 -959,7 +985,7 @@@
959
985
if any(key == node.key for node in pairs):
960
986
raise Exception(f"Key already present: {key.hex()}")
961
987
else:
962
-- if key in hint_keys_values:
988
++ if key_hash(key) in hint_keys_values:
963
989
raise Exception(f"Key already present: {key.hex()}")
964
990

965
991
if reference_node_hash is None:
@@@ -1015,14 -1015,14 +1041,14 @@@
1015
1041
)
1016
1042

1017
1043
if hint_keys_values is not None:
1018
-- hint_keys_values[key] = value
1044
++ hint_keys_values[key_hash(key)] = leaf_hash(key, value)
1019
1045
return InsertResult(node_hash=new_terminal_node_hash, root=new_root)
1020
1046

1021
1047
async def delete(
1022
1048
self,
1023
1049
key: bytes,
1024
1050
tree_id: bytes32,
1025
-- hint_keys_values: Optional[Dict[bytes, bytes]] = None,
1051
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
1026
1052
use_optimized: bool = True,
1027
1053
status: Status = Status.PENDING,
1028
1054
root: Optional[Root] = None,
@@@ -1031,17 -1031,17 +1057,17 @@@
1031
1057
async with self.db_wrapper.writer():
1032
1058
if hint_keys_values is None:
1033
1059
node = await self.get_node_by_key(key=key, tree_id=tree_id)
1060
++ node_hash = node.hash
1061
++ assert isinstance(node, TerminalNode)
1034
1062
else:
1035
-- if key not in hint_keys_values:
1063
++ if key_hash(key) not in hint_keys_values:
1036
1064
log.debug(f"Request to delete an unknown key ignored: {key.hex()}")
1037
1065
return root
1038
-- value = hint_keys_values[key]
1039
-- node_hash = leaf_hash(key=key, value=value)
1040
-- node = TerminalNode(node_hash, key, value)
1041
-- del hint_keys_values[key]
1066
++ node_hash = hint_keys_values[key_hash(key)]
1067
++ del hint_keys_values[key_hash(key)]
1042
1068

1043
1069
ancestors: List[InternalNode] = await self.get_ancestors_common(
1044
-- node_hash=node.hash,
1070
++ node_hash=node_hash,
1045
1071
tree_id=tree_id,
1046
1072
root_hash=root_hash,
1047
1073
use_optimized=use_optimized,
@@@ -1056,7 -1056,7 +1082,7 @@@
1056
1082
)
1057
1083

1058
1084
parent = ancestors[0]
1059
-- other_hash = parent.other_child_hash(hash=node.hash)
1085
++ other_hash = parent.other_child_hash(hash=node_hash)
1060
1086

1061
1087
if len(ancestors) == 1:
1062
1088
# the parent is the root so the other side will become the new root
@@@ -1106,7 -1106,7 +1132,7 @@@
1106
1132
key: bytes,
1107
1133
new_value: bytes,
1108
1134
tree_id: bytes32,
1109
-- hint_keys_values: Optional[Dict[bytes, bytes]] = None,
1135
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
1110
1136
use_optimized: bool = True,
1111
1137
status: Status = Status.PENDING,
1112
1138
root: Optional[Root] = None,
@@@ -1134,7 -1134,7 +1160,7 @@@
1134
1160
return InsertResult(leaf_hash(key, new_value), root)
1135
1161
old_node_hash = old_node.hash
1136
1162
else:
1137
-- if key not in hint_keys_values:
1163
++ if key_hash(key) not in hint_keys_values:
1138
1164
log.debug(f"Key not found: {key.hex()}. Doing an autoinsert instead")
1139
1165
return await self.autoinsert(
1140
1166
key=key,
@@@ -1145,12 -1145,12 +1171,15 @@@
1145
1171
status=status,
1146
1172
root=root,
1147
1173
)
1148
-- value = hint_keys_values[key]
1174
++ node_hash = hint_keys_values[key_hash(key)]
1175
++ node = await self.get_node(node_hash)
1176
++ assert isinstance(node, TerminalNode)
1177
++ value = node.value
1149
1178
if value == new_value:
1150
1179
log.debug(f"New value matches old value in upsert operation: {key.hex()}")
1151
1180
return InsertResult(leaf_hash(key, new_value), root)
1152
1181
old_node_hash = leaf_hash(key=key, value=value)
1153
-- del hint_keys_values[key]
1182
++ del hint_keys_values[key_hash(key)]
1154
1183

1155
1184
# create new terminal node
1156
1185
new_terminal_node_hash = await self._insert_terminal_node(key=key, value=new_value)
@@@ -1192,7 -1192,7 +1221,7 @@@
1192
1221
)
1193
1222

1194
1223
if hint_keys_values is not None:
1195
-- hint_keys_values[key] = new_value
1224
++ hint_keys_values[key_hash(key)] = leaf_hash(key, new_value)
1196
1225
return InsertResult(node_hash=new_terminal_node_hash, root=new_root)
1197
1226

1198
1227
async def clean_node_table(self, writer: aiosqlite.Connection) -> None:
@@@ -1229,7 -1229,7 +1258,7 @@@
1229
1258
if old_root.node_hash is None:
1230
1259
hint_keys_values = {}
1231
1260
else:
1232
-- hint_keys_values = await self.get_keys_values_dict(tree_id, root_hash=root_hash)
1261
++ hint_keys_values = await self.get_keys_values_compressed(tree_id, root_hash=root_hash)
1233
1262

1234
1263
intermediate_root: Optional[Root] = old_root
1235
1264
for change in changelist:
chia/data_layer/util/benchmark.py CHANGED
@@@ -23,7 -23,7 +23,7 @@@ async def generate_datastore(num_nodes
23
23
os.remove(db_path)
24
24

25
25
async with DataStore.managed(database=db_path) as data_store:
26
-- hint_keys_values: Dict[bytes, bytes] = {}
26
++ hint_keys_values: Dict[bytes32, bytes32] = {}
27
27

28
28
tree_id = bytes32(b"0" * 32)
29
29
await data_store.create_tree(tree_id)
chia/full_node/full_node.py CHANGED
@@@ -94,7 -94,7 +94,7 @@@ from chia.util.ints import uint8, uint3
94
94
from chia.util.limited_semaphore import LimitedSemaphore
95
95
from chia.util.log_exceptions import log_exceptions
96
96
from chia.util.path import path_from_root
97
-- from chia.util.profiler import mem_profile_task, profile_task
97
++ from chia.util.profiler import enable_profiler, mem_profile_task, profile_task
98
98
from chia.util.safe_cancel_task import cancel_task_safe
99
99

100
100

@@@ -232,7 -232,7 +232,7 @@@ class FullNode
232
232
async with DBWrapper2.managed(
233
233
self.db_path,
234
234
db_version=db_version,
235
-- reader_count=4,
235
++ reader_count=self.config.get("db_readers", 4),
236
236
log_path=sql_log_path,
237
237
synchronous=db_sync,
238
238
) as self._db_wrapper:
@@@ -288,6 -288,6 +288,13 @@@
288
288
if self.config.get("enable_profiler", False):
289
289
asyncio.create_task(profile_task(self.root_path, "node", self.log))
290
290

291
++ self.profile_block_validation = self.config.get("profile_block_validation", False)
292
++ if self.profile_block_validation: # pragma: no cover
293
++ # this is not covered by any unit tests as it's essentially test code
294
++ # itself. It's exercised manually when investigating performance issues
295
++ profile_dir = path_from_root(self.root_path, "block-validation-profile")
296
++ profile_dir.mkdir(parents=True, exist_ok=True)
297
++
291
298
if self.config.get("enable_memory_profiler", False):
292
299
asyncio.create_task(mem_profile_task(self.root_path, "node", self.log))
293
300

@@@ -466,7 -466,7 +473,7 @@@
466
473
peer = entry.peer
467
474
try:
468
475
inc_status, err = await self.add_transaction(entry.transaction, entry.spend_name, peer, entry.test)
469
-- entry.done.set_result((inc_status, err))
476
++ entry.done.set((inc_status, err))
470
477
except asyncio.CancelledError:
471
478
error_stack = traceback.format_exc()
472
479
self.log.debug(f"Cancelling _handle_one_transaction, closing: {error_stack}")
@@@ -1147,7 -1147,7 +1154,7 @@@
1147
1154
hints_to_add, _ = get_hints_and_subscription_coin_ids(
1148
1155
state_change_summary,
1149
1156
self.subscriptions.has_coin_subscription,
1150
-- self.subscriptions.has_ph_subscription,
1157
++ self.subscriptions.has_puzzle_subscription,
1151
1158
)
1152
1159
await self.hint_store.add_hints(hints_to_add)
1153
1160
# Note that end_height is not necessarily the peak at this
@@@ -1387,8 -1387,8 +1394,8 @@@
1387
1394
self.log.info(
1388
1395
f"⏲️ Finished signage point {request.index_from_challenge}/"
1389
1396
f"{self.constants.NUM_SPS_SUB_SLOT}: "
1390
-- f"CC: {request.challenge_chain_vdf.output.get_hash()} "
1391
-- f"RC: {request.reward_chain_vdf.output.get_hash()} "
1397
++ f"CC: {request.challenge_chain_vdf.output.get_hash().hex()} "
1398
++ f"RC: {request.reward_chain_vdf.output.get_hash().hex()} "
1392
1399
)
1393
1400
self.signage_point_times[request.index_from_challenge] = time.time()
1394
1401
sub_slot_tuple = self.full_node_store.get_sub_slot(request.challenge_chain_vdf.challenge)
@@@ -1456,7 -1456,7 +1463,7 @@@
1456
1463
self.log.info(
1457
1464
f"🌱 Updated peak to height {record.height}, weight {record.weight}, "
1458
1465
f"hh {record.header_hash}, "
1459
-- f"forked at {state_change_summary.fork_height}, rh: {record.reward_infusion_new_challenge}, "
1466
++ f"forked at {state_change_summary.fork_height}, rh: {record.reward_infusion_new_challenge.hex()}, "
1460
1467
f"total iters: {record.total_iters}, "
1461
1468
f"overflow: {record.overflow}, "
1462
1469
f"deficit: {record.deficit}, "
@@@ -1477,7 -1477,7 +1484,7 @@@
1477
1484
hints_to_add, lookup_coin_ids = get_hints_and_subscription_coin_ids(
1478
1485
state_change_summary,
1479
1486
self.subscriptions.has_coin_subscription,
1480
-- self.subscriptions.has_ph_subscription,
1487
++ self.subscriptions.has_puzzle_subscription,
1481
1488
)
1482
1489
await self.hint_store.add_hints(hints_to_add)
1483
1490

@@@ -1591,7 -1591,7 +1598,6 @@@
1591
1598

1592
1599
if record.height % 1000 == 0:
1593
1600
# Occasionally clear data in full node store to keep memory usage small
1594
-- self.full_node_store.clear_seen_unfinished_blocks()
1595
1601
self.full_node_store.clear_old_cache_entries()
1596
1602

1597
1603
if self.sync_store.get_sync_mode() is False:
@@@ -1706,7 -1706,7 +1712,9 @@@
1706
1712
return await self.add_block(new_block, peer)
1707
1713
state_change_summary: Optional[StateChangeSummary] = None
1708
1714
ppp_result: Optional[PeakPostProcessingResult] = None
1709
-- async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high):
1715
++ async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high), enable_profiler(
1716
++ self.profile_block_validation
1717
++ ) as pr:
1710
1718
# After acquiring the lock, check again, because another asyncio thread might have added it
1711
1719
if self.blockchain.contains_block(header_hash):
1712
1720
return None
@@@ -1800,9 -1800,9 +1808,16 @@@
1800
1808
f"pre_validation time: {pre_validation_time:0.2f} seconds, "
1801
1809
f"post-process time: {post_process_time:0.2f} seconds, "
1802
1810
f"cost: {block.transactions_info.cost if block.transactions_info is not None else 'None'}"
1803
-- f"{percent_full_str} header_hash: {header_hash} height: {block.height}",
1811
++ f"{percent_full_str} header_hash: {header_hash.hex()} height: {block.height}",
1804
1812
)
1805
1813

1814
++ # this is not covered by any unit tests as it's essentially test code
1815
++ # itself. It's exercised manually when investigating performance issues
1816
++ if validation_time > 2 and pr is not None: # pragma: no cover
1817
++ pr.create_stats()
1818
++ profile_dir = path_from_root(self.root_path, "block-validation-profile")
1819
++ pr.dump_stats(profile_dir / f"{block.height}-{validation_time:0.1f}.profile")
1820
++
1806
1821
# This code path is reached if added == ADDED_AS_ORPHAN or NEW_TIP
1807
1822
peak = self.blockchain.get_peak()
1808
1823
assert peak is not None
@@@ -1816,11 -1816,11 +1831,17 @@@
1816
1831
"transaction_block": False,
1817
1832
"k_size": block.reward_chain_block.proof_of_space.size,
1818
1833
"header_hash": block.header_hash,
1834
++ "fork_height": None,
1835
++ "rolled_back_records": None,
1819
1836
"height": block.height,
1820
1837
"validation_time": validation_time,
1821
1838
"pre_validation_time": pre_validation_time,
1822
1839
}
1823
1840

1841
++ if state_change_summary is not None:
1842
++ state_changed_data["fork_height"] = state_change_summary.fork_height
1843
++ state_changed_data["rolled_back_records"] = len(state_change_summary.rolled_back_records)
1844
++
1824
1845
if block.transactions_info is not None:
1825
1846
state_changed_data["transaction_block"] = True
1826
1847
state_changed_data["block_cost"] = block.transactions_info.cost
@@@ -1872,7 -1872,7 +1893,7 @@@
1872
1893
if self.full_node_store.seen_unfinished_block(block.get_hash()):
1873
1894
return None
1874
1895

1875
-- block_hash = block.reward_chain_block.get_hash()
1896
++ block_hash = bytes32(block.reward_chain_block.get_hash())
1876
1897

1877
1898
# This searched for the trunk hash (unfinished reward hash). If we have already added a block with the same
1878
1899
# hash, return
@@@ -1954,10 -1954,10 +1975,6 @@@
1954
1975
validation_start = time.monotonic()
1955
1976
validate_result = await self.blockchain.validate_unfinished_block(block, npc_result)
1956
1977
if validate_result.error is not None:
1957
-- if validate_result.error == Err.COIN_AMOUNT_NEGATIVE.value:
1958
-- # TODO: remove in the future, hotfix for 1.1.5 peers to not disconnect older peers
1959
-- self.log.info(f"Consensus error {validate_result.error}, not disconnecting")
1960
-- return
1961
1978
raise ConsensusError(Err(validate_result.error))
1962
1979
validation_time = time.monotonic() - validation_start
1963
1980

@@@ -2097,7 -2097,7 +2114,9 @@@
2097
2114
# If not found, cache keyed on prev block
2098
2115
if prev_b is None:
2099
2116
self.full_node_store.add_to_future_ip(request)
2100
-- self.log.warning(f"Previous block is None, infusion point {request.reward_chain_ip_vdf.challenge}")
2117
++ self.log.warning(
2118
++ f"Previous block is None, infusion point {request.reward_chain_ip_vdf.challenge.hex()}"
2119
++ )
2101
2120
return None
2102
2121

2103
2122
finished_sub_slots: Optional[List[EndOfSubSlotBundle]] = self.full_node_store.get_finished_sub_slots(
@@@ -2128,7 -2128,7 +2147,7 @@@
2128
2147
+ calculate_sp_iters(
2129
2148
self.constants,
2130
2149
sub_slot_iters,
2131
-- unfinished_block.reward_chain_block.signage_point_index,
2150
++ uint8(unfinished_block.reward_chain_block.signage_point_index),
2132
2151
)
2133
2152
)
2134
2153

@@@ -2210,9 -2210,9 +2229,9 @@@
2210
2229
if new_infusions is not None:
2211
2230
self.log.info(
2212
2231
f"⏲️ Finished sub slot, SP {self.constants.NUM_SPS_SUB_SLOT}/{self.constants.NUM_SPS_SUB_SLOT}, "
2213
-- f"{end_of_slot_bundle.challenge_chain.get_hash()}, "
2232
++ f"{end_of_slot_bundle.challenge_chain.get_hash().hex()}, "
2214
2233
f"number of sub-slots: {len(self.full_node_store.finished_sub_slots)}, "
2215
-- f"RC hash: {end_of_slot_bundle.reward_chain.get_hash()}, "
2234
++ f"RC hash: {end_of_slot_bundle.reward_chain.get_hash().hex()}, "
2216
2235
f"Deficit {end_of_slot_bundle.reward_chain.deficit}"
2217
2236
)
2218
2237
# Reset farmer response timer for sub slot (SP 0)
@@@ -2246,7 -2246,7 +2265,7 @@@
2246
2265
else:
2247
2266
self.log.info(
2248
2267
f"End of slot not added CC challenge "
2249
-- f"{end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}"
2268
++ f"{end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}"
2250
2269
)
2251
2270
return None, False
2252
2271

@@@ -2425,8 -2425,8 +2444,8 @@@
2425
2444
if field_vdf == CompressibleVDFField.CC_EOS_VDF:
2426
2445
for index, sub_slot in enumerate(block.finished_sub_slots):
2427
2446
if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf == vdf_info:
2428
-- new_proofs = dataclasses.replace(sub_slot.proofs, challenge_chain_slot_proof=vdf_proof)
2429
-- new_subslot = dataclasses.replace(sub_slot, proofs=new_proofs)
2447
++ new_proofs = sub_slot.proofs.replace(challenge_chain_slot_proof=vdf_proof)
2448
++ new_subslot = sub_slot.replace(proofs=new_proofs)
2430
2449
new_finished_subslots = block.finished_sub_slots
2431
2450
new_finished_subslots[index] = new_subslot
2432
2451
new_block = dataclasses.replace(block, finished_sub_slots=new_finished_subslots)
@@@ -2437,8 -2437,8 +2456,8 @@@
2437
2456
sub_slot.infused_challenge_chain is not None
2438
2457
and sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf == vdf_info
2439
2458
):
2440
-- new_proofs = dataclasses.replace(sub_slot.proofs, infused_challenge_chain_slot_proof=vdf_proof)
2441
-- new_subslot = dataclasses.replace(sub_slot, proofs=new_proofs)
2459
++ new_proofs = sub_slot.proofs.replace(infused_challenge_chain_slot_proof=vdf_proof)
2460
++ new_subslot = sub_slot.replace(proofs=new_proofs)
2442
2461
new_finished_subslots = block.finished_sub_slots
2443
2462
new_finished_subslots[index] = new_subslot
2444
2463
new_block = dataclasses.replace(block, finished_sub_slots=new_finished_subslots)
chia/full_node/full_node_api.py CHANGED
@@@ -576,7 -576,7 +576,7 @@@ class FullNodeAPI
576
576
else:
577
577
if self.full_node.full_node_store.get_sub_slot(request.challenge_hash) is None:
578
578
if request.challenge_hash != self.full_node.constants.GENESIS_CHALLENGE:
579
-- self.log.info(f"Don't have challenge hash {request.challenge_hash}")
579
++ self.log.info(f"Don't have challenge hash {request.challenge_hash.hex()}")
580
580

581
581
sp: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point_by_index(
582
582
request.challenge_hash,
@@@ -651,7 -651,7 +651,8 @@@
651
651
else:
652
652
self.log.debug(
653
653
f"Signage point {request.index_from_challenge} not added, CC challenge: "
654
-- f"{request.challenge_chain_vdf.challenge}, RC challenge: {request.reward_chain_vdf.challenge}"
654
++ f"{request.challenge_chain_vdf.challenge.hex()}, "
655
++ f"RC challenge: {request.reward_chain_vdf.challenge.hex()}"
655
656
)
656
657

657
658
return None
@@@ -706,7 -706,7 +707,7 @@@
706
707
if sp_vdfs.rc_vdf.output.get_hash() != request.reward_chain_sp:
707
708
self.log.debug(
708
709
f"Received proof of space for a potentially old signage point {request.challenge_chain_sp}. "
709
-- f"Current sp: {sp_vdfs.rc_vdf.output.get_hash()}"
710
++ f"Current sp: {sp_vdfs.rc_vdf.output.get_hash().hex()}"
710
711
)
711
712
return None
712
713

@@@ -879,9 -879,9 +880,9 @@@
879
880
sub_slot_iters = peak.sub_slot_iters
880
881
for sub_slot in finished_sub_slots:
881
882
if sub_slot.challenge_chain.new_difficulty is not None:
882
-- difficulty = sub_slot.challenge_chain.new_difficulty
883
++ difficulty = uint64(sub_slot.challenge_chain.new_difficulty)
883
884
if sub_slot.challenge_chain.new_sub_slot_iters is not None:
884
-- sub_slot_iters = sub_slot.challenge_chain.new_sub_slot_iters
885
++ sub_slot_iters = uint64(sub_slot.challenge_chain.new_sub_slot_iters)
885
886

886
887
required_iters: uint64 = calculate_iterations_quality(
887
888
self.full_node.constants.DIFFICULTY_CONSTANT_FACTOR,
@@@ -1012,14 -1012,14 +1013,9 @@@
1012
1013
self.log.warning("Signature not valid. There might be a collision in plots. Ignore this during tests.")
1013
1014
return None
1014
1015

1015
-- fsb2 = dataclasses.replace(
1016
-- candidate.foliage,
1017
-- foliage_block_data_signature=farmer_request.foliage_block_data_signature,
1018
-- )
1016
++ fsb2 = candidate.foliage.replace(foliage_block_data_signature=farmer_request.foliage_block_data_signature)
1019
1017
if candidate.is_transaction_block():
1020
-- fsb2 = dataclasses.replace(
1021
-- fsb2, foliage_transaction_block_signature=farmer_request.foliage_transaction_block_signature
1022
-- )
1018
++ fsb2 = fsb2.replace(foliage_transaction_block_signature=farmer_request.foliage_transaction_block_signature)
1023
1019

1024
1020
new_candidate = dataclasses.replace(candidate, foliage=fsb2)
1025
1021
if not self.full_node.has_valid_pool_sig(new_candidate):
@@@ -1093,7 -1093,7 +1089,7 @@@
1093
1089
if not added:
1094
1090
self.log.error(
1095
1091
f"Was not able to add end of sub-slot: "
1096
-- f"{request.end_of_sub_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}. "
1092
++ f"{request.end_of_sub_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}. "
1097
1093
f"Re-sending new-peak to timelord"
1098
1094
)
1099
1095
await self.full_node.send_peak_to_timelords(peer=peer)
@@@ -1276,7 -1276,7 +1272,7 @@@
1276
1272
await self.full_node.transaction_queue.put(queue_entry, peer_id=None, high_priority=True)
1277
1273
try:
1278
1274
with anyio.fail_after(delay=45):
1279
-- status, error = await queue_entry.done
1275
++ status, error = await queue_entry.done.wait()
1280
1276
except TimeoutError:
1281
1277
response = wallet_protocol.TransactionAck(spend_name, uint8(MempoolInclusionStatus.PENDING), None)
1282
1278
else:
@@@ -1496,7 -1496,7 +1492,7 @@@
1496
1492
# the returned puzzle hashes are the ones we ended up subscribing to.
1497
1493
# It will have filtered duplicates and ones exceeding the subscription
1498
1494
# limit.
1499
-- puzzle_hashes = self.full_node.subscriptions.add_ph_subscriptions(
1495
++ puzzle_hashes = self.full_node.subscriptions.add_puzzle_subscriptions(
1500
1496
peer.peer_node_id, request.puzzle_hashes, max_subscriptions
1501
1497
)
1502
1498

chia/full_node/full_node_store.py CHANGED
@@@ -45,8 -45,8 +45,11 @@@ class FullNodeStore
45
45
candidate_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock]]
46
46
candidate_backup_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock]]
47
47

48
-- # Header hashes of unfinished blocks that we have seen recently
49
-- seen_unfinished_blocks: Set[bytes32]
48
++ # Block hashes of unfinished blocks that we have seen recently. This is
49
++ # effectively a Set[bytes32] but in order to evict the oldest items first,
50
++ # we use a Dict that preserves insertion order, and remove from the
51
++ # beginning
52
++ seen_unfinished_blocks: Dict[bytes32, None]
50
53

51
54
# Unfinished blocks, keyed from reward hash
52
55
unfinished_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock, PreValidationResult]]
@@@ -86,10 -86,10 +89,12 @@@
86
89
serialized_wp_message: Optional[Message]
87
90
serialized_wp_message_tip: Optional[bytes32]
88
91

92
++ max_seen_unfinished_blocks: int
93
++
89
94
def __init__(self, constants: ConsensusConstants):
90
95
self.candidate_blocks = {}
91
96
self.candidate_backup_blocks = {}
92
-- self.seen_unfinished_blocks = set()
97
++ self.seen_unfinished_blocks = {}
93
98
self.unfinished_blocks = {}
94
99
self.finished_sub_slots = []
95
100
self.future_eos_cache = {}
@@@ -108,6 -108,6 +113,7 @@@
108
113
self.tx_fetch_tasks = {}
109
114
self.serialized_wp_message = None
110
115
self.serialized_wp_message_tip = None
116
++ self.max_seen_unfinished_blocks = 1000
111
117

112
118
def add_candidate_block(
113
119
self, quality_string: bytes32, height: uint32, unfinished_block: UnfinishedBlock, backup: bool = False
@@@ -148,12 -148,12 +154,13 @@@
148
154
def seen_unfinished_block(self, object_hash: bytes32) -> bool:
149
155
if object_hash in self.seen_unfinished_blocks:
150
156
return True
151
-- self.seen_unfinished_blocks.add(object_hash)
157
++ self.seen_unfinished_blocks[object_hash] = None
158
++ if len(self.seen_unfinished_blocks) > self.max_seen_unfinished_blocks:
159
++ # remove the least recently added hash
160
++ to_remove = next(iter(self.seen_unfinished_blocks))
161
++ del self.seen_unfinished_blocks[to_remove]
152
162
return False
153
163

154
-- def clear_seen_unfinished_blocks(self) -> None:
155
-- self.seen_unfinished_blocks.clear()
156
--
157
164
def add_unfinished_block(
158
165
self, height: uint32, unfinished_block: UnfinishedBlock, result: PreValidationResult
159
166
) -> None:
@@@ -171,8 -171,8 +178,9 @@@
171
178
return None
172
179
return result[2]
173
180

174
-- def get_unfinished_blocks(self) -> Dict[bytes32, Tuple[uint32, UnfinishedBlock, PreValidationResult]]:
175
-- return self.unfinished_blocks
181
++ # returns all unfinished blocks for the specified height
182
++ def get_unfinished_blocks(self, height: uint32) -> List[UnfinishedBlock]:
183
++ return [block for ub_height, block, _ in self.unfinished_blocks.values() if ub_height == height]
176
184

177
185
def clear_unfinished_blocks_below(self, height: uint32) -> None:
178
186
del_keys: List[bytes32] = []
@@@ -219,7 -219,7 +227,7 @@@
219
227

220
228
self.future_cache_key_times[signage_point.rc_vdf.challenge] = int(time.time())
221
229
self.future_sp_cache[signage_point.rc_vdf.challenge].append((index, signage_point))
222
-- log.info(f"Don't have rc hash {signage_point.rc_vdf.challenge}. caching signage point {index}.")
230
++ log.info(f"Don't have rc hash {signage_point.rc_vdf.challenge.hex()}. caching signage point {index}.")
223
231

224
232
def get_future_ip(self, rc_challenge_hash: bytes32) -> List[timelord_protocol.NewInfusionPointVDF]:
225
233
return self.future_ip_cache.get(rc_challenge_hash, [])
@@@ -287,7 -287,7 +295,7 @@@
287
295
# This prevent other peers from appending fake VDFs to our cache
288
296
log.error(
289
297
f"bad cc_challenge in new_finished_sub_slot, "
290
-- f"got {eos.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}"
298
++ f"got {eos.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}"
291
299
f"expected {cc_challenge}"
292
300
)
293
301
return None
@@@ -310,7 -310,7 +318,7 @@@
310
318
log.debug("dont add slot, total_iters < peak.total_iters")
311
319
return None
312
320

313
-- rc_challenge = eos.reward_chain.end_of_slot_vdf.challenge
321
++ rc_challenge = bytes32(eos.reward_chain.end_of_slot_vdf.challenge)
314
322
cc_start_element = peak.challenge_vdf_output
315
323
iters = uint64(total_iters - peak.total_iters)
316
324
if peak.reward_infusion_new_challenge != rc_challenge:
@@@ -436,9 -436,9 +444,8 @@@
436
444
eos.challenge_chain.challenge_chain_end_of_slot_vdf.output,
437
445
)
438
446
# The EOS will have the whole sub-slot iters, but the proof is only the delta, from the last peak
439
-- if eos.challenge_chain.challenge_chain_end_of_slot_vdf != dataclasses.replace(
440
-- partial_cc_vdf_info,
441
-- number_of_iterations=sub_slot_iters,
447
++ if eos.challenge_chain.challenge_chain_end_of_slot_vdf != partial_cc_vdf_info.replace(
448
++ number_of_iterations=sub_slot_iters
442
449
):
443
450
return None
444
451
if not eos.proofs.challenge_chain_slot_proof.normalized_to_identity and not validate_vdf(
@@@ -487,9 -487,9 +494,8 @@@
487
494
eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
488
495
)
489
496
# The EOS will have the whole sub-slot iters, but the proof is only the delta, from the last peak
490
-- if eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf != dataclasses.replace(
491
-- partial_icc_vdf_info,
492
-- number_of_iterations=icc_iters,
497
++ if eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf != partial_icc_vdf_info.replace(
498
++ number_of_iterations=icc_iters
493
499
):
494
500
return None
495
501
if not eos.proofs.infused_challenge_chain_slot_proof.normalized_to_identity and not validate_vdf(
@@@ -615,9 -615,9 +621,7 @@@
615
621
uint64(sp_total_iters - curr.total_iters),
616
622
signage_point.rc_vdf.output,
617
623
)
618
-- if not signage_point.cc_vdf == dataclasses.replace(
619
-- cc_vdf_info_expected, number_of_iterations=delta_iters
620
-- ):
624
++ if not signage_point.cc_vdf == cc_vdf_info_expected.replace(number_of_iterations=delta_iters):
621
625
self.add_to_future_sp(signage_point, index)
622
626
return False
623
627
if check_from_start_of_ss:
chia/full_node/hint_management.py CHANGED
@@@ -9,7 -9,7 +9,7 @@@ from chia.types.blockchain_format.sized
9
9
def get_hints_and_subscription_coin_ids(
10
10
state_change_summary: StateChangeSummary,
11
11
has_coin_subscription: Callable[[bytes32], bool],
12
-- has_ph_subscription: Callable[[bytes32], bool],
12
++ has_puzzle_subscription: Callable[[bytes32], bool],
13
13
) -> Tuple[List[Tuple[bytes32, bytes]], List[bytes32]]:
14
14
# Precondition: all hints passed in are max 32 bytes long
15
15
# Returns the hints that we need to add to the DB, and the coin ids that need to be looked up
@@@ -26,7 -26,7 +26,7 @@@
26
26
lookup_coin_ids.add(coin_id)
27
27

28
28
def add_if_ph_subscription(puzzle_hash: bytes32, coin_id: bytes32) -> None:
29
-- if has_ph_subscription(puzzle_hash):
29
++ if has_puzzle_subscription(puzzle_hash):
30
30
lookup_coin_ids.add(coin_id)
31
31

32
32
for spend_id, puzzle_hash in state_change_summary.removals:
chia/full_node/subscriptions.py CHANGED
@@@ -9,136 -9,136 +9,196 @@@ from chia.types.blockchain_format.sized
9
9
log = logging.getLogger(__name__)
10
10

11
11

12
-- # The PeerSubscriptions class is essentially a multi-index container. It can be
13
-- # indexed by peer_id, coin_id and puzzle_hash.
12
++ @dataclass(frozen=True)
13
++ class SubscriptionSet:
14
++ _subscriptions_for_peer: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
15
++ _peers_for_subscription: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
16
++
17
++ def add_subscription(self, peer_id: bytes32, item: bytes32) -> bool:
18
++ peers = self._peers_for_subscription.setdefault(item, set())
19
++
20
++ if peer_id in peers:
21
++ return False
22
++
23
++ subscriptions = self._subscriptions_for_peer.setdefault(peer_id, set())
24
++ subscriptions.add(item)
25
++ peers.add(peer_id)
26
++
27
++ return True
28
++
29
++ def remove_subscription(self, peer_id: bytes32, item: bytes32) -> bool:
30
++ subscriptions = self._subscriptions_for_peer.get(peer_id)
31
++
32
++ if subscriptions is None or item not in subscriptions:
33
++ return False
34
++
35
++ peers = self._peers_for_subscription[item]
36
++ peers.remove(peer_id)
37
++ subscriptions.remove(item)
38
++
39
++ if len(subscriptions) == 0:
40
++ self._subscriptions_for_peer.pop(peer_id)
41
++
42
++ if len(peers) == 0:
43
++ self._peers_for_subscription.pop(item)
44
++
45
++ return True
46
++
47
++ def has_subscription(self, item: bytes32) -> bool:
48
++ return item in self._peers_for_subscription
49
++
50
++ def count_subscriptions(self, peer_id: bytes32) -> int:
51
++ return len(self._subscriptions_for_peer.get(peer_id, {}))
52
++
53
++ def remove_peer(self, peer_id: bytes32) -> None:
54
++ for item in self._subscriptions_for_peer.pop(peer_id, {}):
55
++ self._peers_for_subscription[item].remove(peer_id)
56
++
57
++ if len(self._peers_for_subscription[item]) == 0:
58
++ self._peers_for_subscription.pop(item)
59
++
60
++ def subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
61
++ return self._subscriptions_for_peer.get(peer_id, set())
62
++
63
++ def peers(self, item: bytes32) -> Set[bytes32]:
64
++ return self._peers_for_subscription.get(item, set())
65
++
66
++ def total_count(self) -> int:
67
++ return len(self._peers_for_subscription)
68
++
69
++
14
70
@dataclass(frozen=True)
15
71
class PeerSubscriptions:
16
-- # TODO: use NewType all over to describe these various uses of the same types
17
-- # Puzzle Hash : Set[Peer ID]
18
-- _coin_subscriptions: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
19
-- # Puzzle Hash : Set[Peer ID]
20
-- _ph_subscriptions: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
21
-- # Peer ID: Set[Coin ids]
22
-- _peer_coin_ids: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
23
-- # Peer ID: Set[puzzle_hash]
24
-- _peer_puzzle_hash: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
25
-- # Peer ID: subscription count
26
-- _peer_sub_counter: Dict[bytes32, int] = field(default_factory=dict, init=False)
27
--
28
-- def has_ph_subscription(self, ph: bytes32) -> bool:
29
-- return ph in self._ph_subscriptions
72
++ _puzzle_subscriptions: SubscriptionSet = field(default_factory=SubscriptionSet)
73
++ _coin_subscriptions: SubscriptionSet = field(default_factory=SubscriptionSet)
74
++
75
++ def has_puzzle_subscription(self, puzzle_hash: bytes32) -> bool:
76
++ return self._puzzle_subscriptions.has_subscription(puzzle_hash)
30
77

31
78
def has_coin_subscription(self, coin_id: bytes32) -> bool:
32
-- return coin_id in self._coin_subscriptions
79
++ return self._coin_subscriptions.has_subscription(coin_id)
80
++
81
++ def peer_subscription_count(self, peer_id: bytes32) -> int:
82
++ puzzle_subscriptions = self._puzzle_subscriptions.count_subscriptions(peer_id)
83
++ coin_subscriptions = self._coin_subscriptions.count_subscriptions(peer_id)
84
++ return puzzle_subscriptions + coin_subscriptions
33
85

34
-- def add_ph_subscriptions(self, peer_id: bytes32, phs: List[bytes32], max_items: int) -> Set[bytes32]:
86
++ def add_puzzle_subscriptions(self, peer_id: bytes32, puzzle_hashes: List[bytes32], max_items: int) -> Set[bytes32]:
35
87
"""
36
-- returns the puzzle hashes that were actually subscribed to. These may be
37
-- fewer than requested in case:
38
-- * there are duplicate puzzle_hashes
39
-- * some puzzle hashes are already subscribed to
40
-- * the max_items limit is exceeded
88
++ Adds subscriptions until max_items is reached. Filters out duplicates and returns all additions.
41
89
"""
42
90

43
-- puzzle_hash_peers = self._peer_puzzle_hash.setdefault(peer_id, set())
44
-- existing_sub_count = self._peer_sub_counter.setdefault(peer_id, 0)
45
--
46
-- ret: Set[bytes32] = set()
91
++ subscription_count = self.peer_subscription_count(peer_id)
92
++ added: Set[bytes32] = set()
47
93

48
-- # if we've reached the limit on number of subscriptions, just bail
49
-- if existing_sub_count >= max_items:
94
++ def limit_reached() -> Set[bytes32]:
50
95
log.info(
51
-- "peer_id: %s reached max number of puzzle-hash subscriptions. "
52
-- "Not all its coin states will be reported",
96
++ "Peer %s attempted to exceed the subscription limit while adding puzzle subscriptions.",
53
97
peer_id,
54
98
)
55
-- return ret
99
++ return added
56
100

57
-- # decrement this counter as we go, to know if we've hit the limit of
58
-- # number of subscriptions
59
-- subscriptions_left = max_items - existing_sub_count
101
++ # If the subscription limit is reached, bail.
102
++ if subscription_count >= max_items:
103
++ return limit_reached()
60
104

61
-- for ph in phs:
62
-- ph_sub = self._ph_subscriptions.setdefault(ph, set())
63
-- if peer_id in ph_sub:
105
++ # Decrement this counter to know if we've hit the subscription limit.
106
++ subscriptions_left = max_items - subscription_count
107
++
108
++ for puzzle_hash in puzzle_hashes:
109
++ if not self._puzzle_subscriptions.add_subscription(peer_id, puzzle_hash):
64
110
continue
65
111

66
-- ret.add(ph)
67
-- ph_sub.add(peer_id)
68
-- puzzle_hash_peers.add(ph)
69
-- self._peer_sub_counter[peer_id] += 1
70
112
subscriptions_left -= 1
113
++ added.add(puzzle_hash)
71
114

72
115
if subscriptions_left == 0:
73
-- log.info(
74
-- "peer_id: %s reached max number of puzzle-hash subscriptions. "
75
-- "Not all its coin states will be reported",
76
-- peer_id,
77
-- )
78
-- break
79
-- return ret
80
--
81
-- def add_coin_subscriptions(self, peer_id: bytes32, coin_ids: List[bytes32], max_items: int) -> None:
82
-- coin_id_peers = self._peer_coin_ids.setdefault(peer_id, set())
83
-- existing_sub_count = self._peer_sub_counter.setdefault(peer_id, 0)
84
--
85
-- # if we've reached the limit on number of subscriptions, just bail
86
-- if existing_sub_count >= max_items:
116
++ return limit_reached()
117
++
118
++ return added
119
++
120
++ def add_coin_subscriptions(self, peer_id: bytes32, coin_ids: List[bytes32], max_items: int) -> Set[bytes32]:
121
++ """
122
++ Adds subscriptions until max_items is reached. Filters out duplicates and returns all additions.
123
++ """
124
++
125
++ subscription_count = self.peer_subscription_count(peer_id)
126
++ added: Set[bytes32] = set()
127
++
128
++ def limit_reached() -> Set[bytes32]:
87
129
log.info(
88
-- "peer_id: %s reached max number of coin subscriptions. Not all its coin states will be reported",
130
++ "Peer %s attempted to exceed the subscription limit while adding coin subscriptions.",
89
131
peer_id,
90
132
)
91
-- return
133
++ return added
92
134

93
-- # decrement this counter as we go, to know if we've hit the limit of
94
-- # number of subscriptions
95
-- subscriptions_left = max_items - existing_sub_count
135
++ # If the subscription limit is reached, bail.
136
++ if subscription_count >= max_items:
137
++ return limit_reached()
138
++
139
++ # Decrement this counter to know if we've hit the subscription limit.
140
++ subscriptions_left = max_items - subscription_count
96
141

97
142
for coin_id in coin_ids:
98
-- coin_sub = self._coin_subscriptions.setdefault(coin_id, set())
99
-- if peer_id in coin_sub:
143
++ if not self._coin_subscriptions.add_subscription(peer_id, coin_id):
100
144
continue
101
145

102
-- coin_sub.add(peer_id)
103
-- coin_id_peers.add(coin_id)
104
-- self._peer_sub_counter[peer_id] += 1
105
146
subscriptions_left -= 1
147
++ added.add(coin_id)
106
148

107
149
if subscriptions_left == 0:
108
-- log.info(
109
-- "peer_id: %s reached max number of coin subscriptions. Not all its coin states will be reported",
110
-- peer_id,
111
-- )
112
-- break
150
++ return limit_reached()
151
++
152
++ return added
153
++
154
++ def remove_puzzle_subscriptions(self, peer_id: bytes32, puzzle_hashes: List[bytes32]) -> Set[bytes32]:
155
++ """
156
++ Removes subscriptions. Filters out duplicates and returns all removals.
157
++ """
158
++
159
++ removed: Set[bytes32] = set()
160
++
161
++ for puzzle_hash in puzzle_hashes:
162
++ if not self._puzzle_subscriptions.remove_subscription(peer_id, puzzle_hash):
163
++ continue
164
++
165
++ removed.add(puzzle_hash)
166
++
167
++ return removed
168
++
169
++ def remove_coin_subscriptions(self, peer_id: bytes32, coin_ids: List[bytes32]) -> Set[bytes32]:
170
++ """
171
++ Removes subscriptions. Filters out duplicates and returns all removals.
172
++ """
173
++
174
++ removed: Set[bytes32] = set()
175
++
176
++ for coin_id in coin_ids:
177
++ if not self._coin_subscriptions.remove_subscription(peer_id, coin_id):
178
++ continue
179
++
180
++ removed.add(coin_id)
181
++
182
++ return removed
113
183

114
184
def remove_peer(self, peer_id: bytes32) -> None:
115
-- counter = 0
116
-- puzzle_hashes = self._peer_puzzle_hash.get(peer_id)
117
-- if puzzle_hashes is not None:
118
-- for ph in puzzle_hashes:
119
-- subs = self._ph_subscriptions[ph]
120
-- subs.remove(peer_id)
121
-- counter += 1
122
-- if subs == set():
123
-- self._ph_subscriptions.pop(ph)
124
-- self._peer_puzzle_hash.pop(peer_id)
125
--
126
-- coin_ids = self._peer_coin_ids.get(peer_id)
127
-- if coin_ids is not None:
128
-- for coin_id in coin_ids:
129
-- subs = self._coin_subscriptions[coin_id]
130
-- subs.remove(peer_id)
131
-- counter += 1
132
-- if subs == set():
133
-- self._coin_subscriptions.pop(coin_id)
134
-- self._peer_coin_ids.pop(peer_id)
135
--
136
-- if peer_id in self._peer_sub_counter:
137
-- num_subs = self._peer_sub_counter.pop(peer_id)
138
-- assert num_subs == counter
185
++ self._puzzle_subscriptions.remove_peer(peer_id)
186
++ self._coin_subscriptions.remove_peer(peer_id)
187
++
188
++ def coin_subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
189
++ return self._coin_subscriptions.subscriptions(peer_id)
190
++
191
++ def puzzle_subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
192
++ return self._puzzle_subscriptions.subscriptions(peer_id)
139
193

140
194
def peers_for_coin_id(self, coin_id: bytes32) -> Set[bytes32]:
141
-- return self._coin_subscriptions.get(coin_id, set())
195
++ return self._coin_subscriptions.peers(coin_id)
142
196

143
197
def peers_for_puzzle_hash(self, puzzle_hash: bytes32) -> Set[bytes32]:
144
-- return self._ph_subscriptions.get(puzzle_hash, set())
198
++ return self._puzzle_subscriptions.peers(puzzle_hash)
199
++
200
++ def coin_subscription_count(self) -> int:
201
++ return self._coin_subscriptions.total_count()
202
++
203
++ def puzzle_subscription_count(self) -> int:
204
++ return self._puzzle_subscriptions.total_count()
chia/full_node/weight_proof.py ADDED
@@@ -442,7 -442,7 +442,7 @@@ class WeightProofHandler
442
442
None,
443
443
None,
444
444
None,
445
-- curr.reward_chain_block.signage_point_index,
445
++ uint8(curr.reward_chain_block.signage_point_index),
446
446
None,
447
447
None,
448
448
None,
@@@ -549,7 -549,7 +549,7 @@@
549
549
curr.challenge_chain_ip_proof,
550
550
icc_ip_proof,
551
551
cc_sp_info,
552
-- curr.reward_chain_block.signage_point_index,
552
++ uint8(curr.reward_chain_block.signage_point_index),
553
553
None,
554
554
None,
555
555
None,
@@@ -565,7 -565,7 +565,7 @@@
565
565
if len(weight_proof.sub_epochs) == 0:
566
566
return False, uint32(0)
567
567

568
-- peak_height = weight_proof.recent_chain_data[-1].reward_chain_block.height
568
++ peak_height = uint32(weight_proof.recent_chain_data[-1].reward_chain_block.height)
569
569
log.info(f"validate weight proof peak height {peak_height}")
570
570
summaries, sub_epoch_weight_list = _validate_sub_epoch_summaries(self.constants, weight_proof)
571
571
if summaries is None:
@@@ -707,10 -707,10 +707,10 @@@ def _create_sub_epoch_data
707
707
) -> SubEpochData:
708
708
reward_chain_hash: bytes32 = sub_epoch_summary.reward_chain_hash
709
709
# Number of subblocks overflow in previous slot
710
-- previous_sub_epoch_overflows: uint8 = sub_epoch_summary.num_blocks_overflow # total in sub epoch - expected
710
++ previous_sub_epoch_overflows = uint8(sub_epoch_summary.num_blocks_overflow) # total in sub epoch - expected
711
711
# New work difficulty and iterations per sub-slot
712
-- sub_slot_iters: Optional[uint64] = sub_epoch_summary.new_sub_slot_iters
713
-- new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty
712
++ sub_slot_iters: Optional[int] = sub_epoch_summary.new_sub_slot_iters
713
++ new_difficulty: Optional[int] = sub_epoch_summary.new_difficulty
714
714
return SubEpochData(reward_chain_hash, previous_sub_epoch_overflows, sub_slot_iters, new_difficulty)
715
715

716
716

@@@ -746,7 -746,7 +746,7 @@@ async def _challenge_block_vdfs
746
746
header_block.challenge_chain_ip_proof,
747
747
None,
748
748
cc_sp_info,
749
-- header_block.reward_chain_block.signage_point_index,
749
++ uint8(header_block.reward_chain_block.signage_point_index),
750
750
None,
751
751
None,
752
752
None,
@@@ -886,7 -886,7 +886,7 @@@ def _map_sub_epoch_summaries
886
886

887
887
# if new epoch update diff and iters
888
888
if data.new_difficulty is not None:
889
-- curr_difficulty = data.new_difficulty
889
++ curr_difficulty = uint64(data.new_difficulty)
890
890

891
891
# add to dict
892
892
summaries.append(ses)
@@@ -998,7 -998,7 +998,7 @@@ def _validate_segment
998
998
return False, uint64(0), uint64(0), uint64(0), []
999
999
assert sub_slot_data.signage_point_index is not None
1000
1000
ip_iters = ip_iters + calculate_ip_iters(
1001
-- constants, curr_ssi, sub_slot_data.signage_point_index, required_iters
1001
++ constants, curr_ssi, uint8(sub_slot_data.signage_point_index), required_iters
1002
1002
)
1003
1003
vdf_list = _get_challenge_block_vdfs(constants, idx, segment.sub_slots, curr_ssi)
1004
1004
to_validate.extend(vdf_list)
@@@ -1025,7 -1025,7 +1025,7 @@@ def _get_challenge_block_vdfs
1025
1025
assert sub_slot_data.signage_point_index
1026
1026
sp_input = ClassgroupElement.get_default_element()
1027
1027
if not sub_slot_data.cc_signage_point.normalized_to_identity and sub_slot_idx >= 1:
1028
-- is_overflow = is_overflow_block(constants, sub_slot_data.signage_point_index)
1028
++ is_overflow = is_overflow_block(constants, uint8(sub_slot_data.signage_point_index))
1029
1029
prev_ssd = sub_slots[sub_slot_idx - 1]
1030
1030
sp_input = sub_slot_data_vdf_input(
1031
1031
constants, sub_slot_data, sub_slot_idx, sub_slots, is_overflow, prev_ssd.is_end_of_slot(), ssi
@@@ -1103,7 -1103,7 +1103,7 @@@ def _validate_sub_slot_data
1103
1103
assert sub_slot_data.cc_sp_vdf_info
1104
1104
input = ClassgroupElement.get_default_element()
1105
1105
if not sub_slot_data.cc_signage_point.normalized_to_identity:
1106
-- is_overflow = is_overflow_block(constants, sub_slot_data.signage_point_index)
1106
++ is_overflow = is_overflow_block(constants, uint8(sub_slot_data.signage_point_index))
1107
1107
input = sub_slot_data_vdf_input(
1108
1108
constants, sub_slot_data, sub_slot_idx, sub_slots, is_overflow, prev_ssd.is_end_of_slot(), ssi
1109
1109
)
@@@ -1208,9 -1208,9 +1208,9 @@@ def validate_recent_blocks
1208
1208
last_blocks_to_validate = 100 # todo remove cap after benchmarks
1209
1209
for summary in summaries[:ses_idx]:
1210
1210
if summary.new_sub_slot_iters is not None:
1211
-- ssi = summary.new_sub_slot_iters
1211
++ ssi = uint64(summary.new_sub_slot_iters)
1212
1212
if summary.new_difficulty is not None:
1213
-- diff = summary.new_difficulty
1213
++ diff = uint64(summary.new_difficulty)
1214
1214

1215
1215
ses_blocks, sub_slots, transaction_blocks = 0, 0, 0
1216
1216
challenge, prev_challenge = recent_chain.recent_chain_data[0].reward_chain_block.pos_ss_cc_challenge_hash, None
@@@ -1226,18 -1226,18 +1226,18 @@@
1226
1226
for sub_slot in block.finished_sub_slots:
1227
1227
prev_challenge = sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.challenge
1228
1228
challenge = sub_slot.challenge_chain.get_hash()
1229
-- deficit = sub_slot.reward_chain.deficit
1229
++ deficit = uint8(sub_slot.reward_chain.deficit)
1230
1230
if sub_slot.challenge_chain.subepoch_summary_hash is not None:
1231
1231
ses = True
1232
1232
assert summaries[ses_idx].get_hash() == sub_slot.challenge_chain.subepoch_summary_hash
1233
1233
ses_idx += 1
1234
1234
if sub_slot.challenge_chain.new_sub_slot_iters is not None:
1235
-- ssi = sub_slot.challenge_chain.new_sub_slot_iters
1235
++ ssi = uint64(sub_slot.challenge_chain.new_sub_slot_iters)
1236
1236
if sub_slot.challenge_chain.new_difficulty is not None:
1237
-- diff = sub_slot.challenge_chain.new_difficulty
1237
++ diff = uint64(sub_slot.challenge_chain.new_difficulty)
1238
1238

1239
1239
if (challenge is not None) and (prev_challenge is not None):
1240
-- overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
1240
++ overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
1241
1241
if not adjusted:
1242
1242
assert prev_block_record is not None
1243
1243
prev_block_record = dataclasses.replace(
@@@ -1334,7 -1334,7 +1334,7 @@@ def __validate_pospace
1334
1334

1335
1335
sub_slot_data: SubSlotData = segment.sub_slots[idx]
1336
1336

1337
-- if sub_slot_data.signage_point_index and is_overflow_block(constants, sub_slot_data.signage_point_index):
1337
++ if sub_slot_data.signage_point_index and is_overflow_block(constants, uint8(sub_slot_data.signage_point_index)):
1338
1338
curr_slot = segment.sub_slots[idx - 1]
1339
1339
assert curr_slot.cc_slot_end_info
1340
1340
challenge = curr_slot.cc_slot_end_info.challenge
@@@ -1391,14 -1391,14 +1391,14 @@@ def __get_rc_sub_slot
1391
1391
slots_n = 1
1392
1392
assert first
1393
1393
assert first.signage_point_index is not None
1394
-- if is_overflow_block(constants, first.signage_point_index):
1394
++ if is_overflow_block(constants, uint8(first.signage_point_index)):
1395
1395
if idx >= 2 and slots[idx - 2].cc_slot_end is None:
1396
1396
slots_n = 2
1397
1397

1398
1398
new_diff = None if ses is None else ses.new_difficulty
1399
1399
new_ssi = None if ses is None else ses.new_sub_slot_iters
1400
1400
ses_hash: Optional[bytes32] = None if ses is None else ses.get_hash()
1401
-- overflow = is_overflow_block(constants, first.signage_point_index)
1401
++ overflow = is_overflow_block(constants, uint8(first.signage_point_index))
1402
1402
if overflow:
1403
1403
if idx >= 2 and slots[idx - 2].cc_slot_end is not None and slots[idx - 1].cc_slot_end is not None:
1404
1404
ses_hash = None
@@@ -1483,9 -1483,9 +1483,9 @@@ def _get_curr_diff_ssi
1483
1483
curr_ssi = constants.SUB_SLOT_ITERS_STARTING
1484
1484
for ses in reversed(summaries[0:idx]):
1485
1485
if ses.new_sub_slot_iters is not None:
1486
-- curr_ssi = ses.new_sub_slot_iters
1486
++ curr_ssi = uint64(ses.new_sub_slot_iters)
1487
1487
assert ses.new_difficulty is not None
1488
-- curr_difficulty = ses.new_difficulty
1488
++ curr_difficulty = uint64(ses.new_difficulty)
1489
1489
break
1490
1490

1491
1491
return curr_difficulty, curr_ssi
@@@ -1521,7 -1521,7 +1521,7 @@@ def _get_last_ses_hash
1521
1521
if slot.challenge_chain.subepoch_summary_hash is not None:
1522
1522
return (
1523
1523
slot.challenge_chain.subepoch_summary_hash,
1524
-- curr.reward_chain_block.height,
1524
++ uint32(curr.reward_chain_block.height),
1525
1525
)
1526
1526
idx += 1
1527
1527
return None, uint32(0)
@@@ -1558,8 -1558,8 +1558,8 @@@ def get_sp_total_iters
1558
1558
assert sub_slot_data.cc_ip_vdf_info is not None
1559
1559
assert sub_slot_data.total_iters is not None
1560
1560
assert sub_slot_data.signage_point_index is not None
1561
-- sp_iters: uint64 = calculate_sp_iters(constants, ssi, sub_slot_data.signage_point_index)
1562
-- ip_iters: uint64 = sub_slot_data.cc_ip_vdf_info.number_of_iterations
1561
++ sp_iters: uint64 = calculate_sp_iters(constants, ssi, uint8(sub_slot_data.signage_point_index))
1562
++ ip_iters: uint64 = uint64(sub_slot_data.cc_ip_vdf_info.number_of_iterations)
1563
1563
sp_sub_slot_total_iters = uint128(sub_slot_data.total_iters - ip_iters)
1564
1564
if is_overflow:
1565
1565
sp_sub_slot_total_iters = uint128(sp_sub_slot_total_iters - ssi)
@@@ -1645,7 -1645,7 +1645,7 @@@ async def validate_weight_proof_inner
1645
1645
if len(weight_proof.sub_epochs) == 0:
1646
1646
return False, []
1647
1647

1648
-- peak_height = weight_proof.recent_chain_data[-1].reward_chain_block.height
1648
++ peak_height = uint32(weight_proof.recent_chain_data[-1].reward_chain_block.height)
1649
1649
log.info(f"validate weight proof peak height {peak_height}")
1650
1650
seed = summaries[-2].get_hash()
1651
1651
rng = random.Random(seed)
1652
-- - /dev/null
1652
++ + b/chia/legacy/__init__.py
chia/plotting/cache.py CHANGED
@@@ -138,6 -138,6 +138,16 @@@ class Cache
138
138
cache_data: CacheDataV1 = CacheDataV1.from_bytes(stored_cache.blob)
139
139
self._data = {}
140
140
estimated_c2_sizes: Dict[int, int] = {}
141
++ measured_sizes: Dict[int, int] = {
142
++ 32: 738,
143
++ 33: 1083,
144
++ 34: 1771,
145
++ 35: 3147,
146
++ 36: 5899,
147
++ 37: 11395,
148
++ 38: 22395,
149
++ 39: 44367,
150
++ }
141
151
for path, cache_entry in cache_data.entries:
142
152
new_entry = CacheEntry(
143
153
DiskProver.from_bytes(cache_entry.prover_data),
@@@ -160,7 -160,7 +170,14 @@@
160
170
# static data: version(2) + table pointers (<=96) + id(32) + k(1) => ~130
161
171
# path: up to ~1870, all above will lead to false positive.
162
172
# See https://github.com/Chia-Network/chiapos/blob/3ee062b86315823dd775453ad320b8be892c7df3/src/prover_disk.hpp#L282-L287 # noqa: E501
163
-- if prover_size > (estimated_c2_sizes[k] + memo_size + 2000):
173
++
174
++ # Use experimental measurements if more than estimates
175
++ # https://github.com/Chia-Network/chia-blockchain/issues/16063
176
++ check_size = estimated_c2_sizes[k] + memo_size + 2000
177
++ if k in measured_sizes:
178
++ check_size = max(check_size, measured_sizes[k])
179
++
180
++ if prover_size > check_size:
164
181
log.warning(
165
182
"Suspicious cache entry dropped. Recommended: stop the harvester, remove "
166
183
f"{self._path}, restart. Entry: size {prover_size}, path {path}"
chia/pools/pool_puzzles.py CHANGED
@@@ -4,7 -4,7 +4,7 @@@ import loggin
4
4
from typing import List, Optional, Tuple
5
5

6
6
from chia_rs import G1Element
7
-- from clvm.casts import int_from_bytes, int_to_bytes
7
++ from clvm.casts import int_from_bytes
8
8

9
9
from chia.clvm.singleton import SINGLETON_LAUNCHER
10
10
from chia.consensus.block_rewards import calculate_pool_reward
@@@ -93,7 -93,7 +93,7 @@@ def create_p2_singleton_puzzle
93
93

94
94
def launcher_id_to_p2_puzzle_hash(launcher_id: bytes32, seconds_delay: uint64, delayed_puzzle_hash: bytes32) -> bytes32:
95
95
return create_p2_singleton_puzzle(
96
-- SINGLETON_MOD_HASH, launcher_id, int_to_bytes(seconds_delay), delayed_puzzle_hash
96
++ SINGLETON_MOD_HASH, launcher_id, seconds_delay, delayed_puzzle_hash
97
97
).get_tree_hash()
98
98

99
99

chia/rpc/full_node_rpc_api.py CHANGED
@@@ -102,6 -102,6 +102,7 @@@ class FullNodeRpcApi
102
102
"/get_unfinished_block_headers": self.get_unfinished_block_headers,
103
103
"/get_network_space": self.get_network_space,
104
104
"/get_additions_and_removals": self.get_additions_and_removals,
105
++ "/get_aggsig_additional_data": self.get_aggsig_additional_data,
105
106
# this function is just here for backwards-compatibility. It will probably
106
107
# be removed in the future
107
108
"/get_initial_freeze_period": self.get_initial_freeze_period,
@@@ -556,18 -556,18 +557,17 @@@
556
557
return {"headers": []}
557
558

558
559
response_headers: List[UnfinishedHeaderBlock] = []
559
-- for ub_height, block, _ in (self.service.full_node_store.get_unfinished_blocks()).values():
560
-- if ub_height == peak.height:
561
-- unfinished_header_block = UnfinishedHeaderBlock(
562
-- block.finished_sub_slots,
563
-- block.reward_chain_block,
564
-- block.challenge_chain_sp_proof,
565
-- block.reward_chain_sp_proof,
566
-- block.foliage,
567
-- block.foliage_transaction_block,
568
-- b"",
569
-- )
570
-- response_headers.append(unfinished_header_block)
560
++ for block in self.service.full_node_store.get_unfinished_blocks(peak.height):
561
++ unfinished_header_block = UnfinishedHeaderBlock(
562
++ block.finished_sub_slots,
563
++ block.reward_chain_block,
564
++ block.challenge_chain_sp_proof,
565
++ block.reward_chain_sp_proof,
566
++ block.foliage,
567
++ block.foliage_transaction_block,
568
++ b"",
569
++ )
570
++ response_headers.append(unfinished_header_block)
571
571
return {"headers": response_headers}
572
572

573
573
async def get_network_space(self, request: Dict[str, Any]) -> EndpointResult:
@@@ -806,6 -806,6 +806,9 @@@
806
806
"removals": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in removals],
807
807
}
808
808

809
++ async def get_aggsig_additional_data(self, _: Dict[str, Any]) -> EndpointResult:
810
++ return {"additional_data": self.service.constants.AGG_SIG_ME_ADDITIONAL_DATA.hex()}
811
++
809
812
async def get_all_mempool_tx_ids(self, _: Dict[str, Any]) -> EndpointResult:
810
813
ids = list(self.service.mempool_manager.mempool.all_item_ids())
811
814
return {"tx_ids": ids}
chia/rpc/full_node_rpc_client.py CHANGED
@@@ -159,6 -159,6 +159,10 @@@ class FullNodeRpcClient(RpcClient)
159
159
response = await self.fetch("get_coin_records_by_parent_ids", d)
160
160
return [CoinRecord.from_json_dict(coin_record_dict_backwards_compat(coin)) for coin in response["coin_records"]]
161
161

162
++ async def get_aggsig_additional_data(self) -> bytes32:
163
++ result = await self.fetch("get_aggsig_additional_data", {})
164
++ return bytes32.from_hexstr(result["additional_data"])
165
++
162
166
async def get_coin_records_by_hint(
163
167
self,
164
168
hint: bytes32,
chia/rpc/wallet_rpc_api.py CHANGED
@@@ -3498,7 -3494,7 +3498,7 @@@ class WalletRpcApi
3498
3498
full_puzzle = nft_puzzles.create_full_puzzle(
3499
3499
uncurried_nft.singleton_launcher_id,
3500
3500
metadata,
3501
-- uncurried_nft.metadata_updater_hash,
3501
++ bytes32(uncurried_nft.metadata_updater_hash.as_atom()),
3502
3502
inner_puzzle,
3503
3503
)
3504
3504

chia/rpc/wallet_rpc_client.py CHANGED
@@@ -1,6 -1,6 +1,6 @@@
1
1
from __future__ import annotations
2
2

3
-- from typing import Any, Dict, List, Optional, Tuple, Union
3
++ from typing import Any, Dict, List, Optional, Tuple, Union, cast
4
4

5
5
from chia.data_layer.data_layer_wallet import Mirror, SingletonRecord
6
6
from chia.pools.pool_wallet_info import PoolWalletInfo
@@@ -9,6 -9,6 +9,7 @@@ from chia.types.blockchain_format.coin
9
9
from chia.types.blockchain_format.program import Program
10
10
from chia.types.blockchain_format.sized_bytes import bytes32
11
11
from chia.types.coin_record import CoinRecord
12
++ from chia.types.spend_bundle import SpendBundle
12
13
from chia.util.bech32m import encode_puzzle_hash
13
14
from chia.util.ints import uint16, uint32, uint64
14
15
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts
@@@ -41,97 -41,97 +42,117 @@@ class WalletRpcClient(RpcClient)
41
42
"""
42
43

43
44
# Key Management APIs
44
-- async def log_in(self, fingerprint: int) -> Dict:
45
++ async def log_in(self, fingerprint: int) -> Union[Dict[str, Any], Any]:
45
46
try:
46
-- return await self.fetch(
47
-- "log_in",
48
-- {"fingerprint": fingerprint, "type": "start"},
49
-- )
50
--
47
++ return await self.fetch("log_in", {"fingerprint": fingerprint, "type": "start"})
51
48
except ValueError as e:
52
49
return e.args[0]
53
50

54
51
async def set_wallet_resync_on_startup(self, enable: bool = True) -> Dict[str, Any]:
55
-- return await self.fetch(
56
-- path="set_wallet_resync_on_startup",
57
-- request_json={"enable": enable},
58
-- )
52
++ return await self.fetch(path="set_wallet_resync_on_startup", request_json={"enable": enable})
59
53

60
-- async def get_logged_in_fingerprint(self) -> int:
61
-- return (await self.fetch("get_logged_in_fingerprint", {}))["fingerprint"]
54
++ async def get_logged_in_fingerprint(self) -> Optional[int]:
55
++ response = await self.fetch("get_logged_in_fingerprint", {})
56
++ # TODO: casting due to lack of type checked deserialization
57
++ return cast(Optional[int], response["fingerprint"])
62
58

63
59
async def get_public_keys(self) -> List[int]:
64
-- return (await self.fetch("get_public_keys", {}))["public_key_fingerprints"]
60
++ response = await self.fetch("get_public_keys", {})
61
++ # TODO: casting due to lack of type checked deserialization
62
++ return cast(List[int], response["public_key_fingerprints"])
65
63

66
-- async def get_private_key(self, fingerprint: int) -> Dict:
67
-- return (await self.fetch("get_private_key", {"fingerprint": fingerprint}))["private_key"]
64
++ async def get_private_key(self, fingerprint: int) -> Dict[str, Any]:
65
++ request = {"fingerprint": fingerprint}
66
++ response = await self.fetch("get_private_key", request)
67
++ # TODO: casting due to lack of type checked deserialization
68
++ return cast(Dict[str, Any], response["private_key"])
68
69

69
70
async def generate_mnemonic(self) -> List[str]:
70
-- return (await self.fetch("generate_mnemonic", {}))["mnemonic"]
71
++ response = await self.fetch("generate_mnemonic", {})
72
++ # TODO: casting due to lack of type checked deserialization
73
++ return cast(List[str], response["mnemonic"])
71
74

72
75
async def add_key(self, mnemonic: List[str], request_type: str = "new_wallet") -> Dict[str, Any]:
73
-- return await self.fetch("add_key", {"mnemonic": mnemonic, "type": request_type})
76
++ request = {"mnemonic": mnemonic, "type": request_type}
77
++ return await self.fetch("add_key", request)
74
78

75
79
async def delete_key(self, fingerprint: int) -> Dict[str, Any]:
76
-- return await self.fetch("delete_key", {"fingerprint": fingerprint})
80
++ request = {"fingerprint": fingerprint}
81
++ return await self.fetch("delete_key", request)
77
82

78
83
async def check_delete_key(self, fingerprint: int, max_ph_to_search: int = 100) -> Dict[str, Any]:
79
-- return await self.fetch("check_delete_key", {"fingerprint": fingerprint, "max_ph_to_search": max_ph_to_search})
84
++ request = {"fingerprint": fingerprint, "max_ph_to_search": max_ph_to_search}
85
++ return await self.fetch("check_delete_key", request)
80
86

81
87
async def delete_all_keys(self) -> Dict[str, Any]:
82
88
return await self.fetch("delete_all_keys", {})
83
89

84
90
# Wallet Node APIs
85
91
async def get_sync_status(self) -> bool:
86
-- return (await self.fetch("get_sync_status", {}))["syncing"]
92
++ response = await self.fetch("get_sync_status", {})
93
++ # TODO: casting due to lack of type checked deserialization
94
++ return cast(bool, response["syncing"])
87
95

88
96
async def get_synced(self) -> bool:
89
-- return (await self.fetch("get_sync_status", {}))["synced"]
97
++ response = await self.fetch("get_sync_status", {})
98
++ # TODO: casting due to lack of type checked deserialization
99
++ return cast(bool, response["synced"])
90
100

91
101
async def get_height_info(self) -> uint32:
92
-- return (await self.fetch("get_height_info", {}))["height"]
102
++ response = await self.fetch("get_height_info", {})
103
++ # TODO: casting due to lack of type checked deserialization
104
++ return cast(uint32, response["height"])
93
105

94
-- async def push_tx(self, spend_bundle):
106
++ async def push_tx(self, spend_bundle: SpendBundle) -> Dict[str, Any]:
95
107
return await self.fetch("push_tx", {"spend_bundle": bytes(spend_bundle).hex()})
96
108

97
-- async def push_transactions(self, txs: List[TransactionRecord]):
109
++ async def push_transactions(self, txs: List[TransactionRecord]) -> Dict[str, Any]:
98
110
transactions = [bytes(tx).hex() for tx in txs]
99
--
100
111
return await self.fetch("push_transactions", {"transactions": transactions})
101
112

102
113
async def farm_block(self, address: str) -> Dict[str, Any]:
103
114
return await self.fetch("farm_block", {"address": address})
104
115

105
116
async def get_timestamp_for_height(self, height: uint32) -> uint64:
106
-- return uint64((await self.fetch("get_timestamp_for_height", {"height": height}))["timestamp"])
117
++ request = {"height": height}
118
++ response = await self.fetch("get_timestamp_for_height", request)
119
++ # TODO: casting due to lack of type checked deserialization
120
++ return cast(uint64, response["timestamp"])
107
121

108
122
# Wallet Management APIs
109
123
async def get_wallets(self, wallet_type: Optional[WalletType] = None) -> List[Dict[str, Any]]:
110
-- request: Dict[str, Any] = {}
111
-- if wallet_type is not None:
112
-- request["type"] = wallet_type
113
-- return (await self.fetch("get_wallets", request))["wallets"]
124
++ if wallet_type is None:
125
++ request = {}
126
++ else:
127
++ request = {"type": wallet_type}
128
++ response = await self.fetch("get_wallets", request)
129
++ # TODO: casting due to lack of type checked deserialization
130
++ return cast(List[Dict[str, Any]], response["wallets"])
114
131

115
132
# Wallet APIs
116
-- async def get_wallet_balance(self, wallet_id: int) -> Dict:
117
-- return (await self.fetch("get_wallet_balance", {"wallet_id": wallet_id}))["wallet_balance"]
133
++ async def get_wallet_balance(self, wallet_id: int) -> Dict[str, Any]:
134
++ request = {"wallet_id": wallet_id}
135
++ response = await self.fetch("get_wallet_balance", request)
136
++ # TODO: casting due to lack of type checked deserialization
137
++ return cast(Dict[str, Any], response["wallet_balance"])
118
138

119
-- async def get_wallet_balances(self, wallet_ids: Optional[List[int]] = None) -> Dict:
120
-- return (await self.fetch("get_wallet_balances", {"wallet_ids": wallet_ids}))["wallet_balances"]
139
++ async def get_wallet_balances(self, wallet_ids: Optional[List[int]] = None) -> Dict[str, Dict[str, Any]]:
140
++ request = {"wallet_ids": wallet_ids}
141
++ response = await self.fetch("get_wallet_balances", request)
142
++ # TODO: casting due to lack of type checked deserialization
143
++ return cast(Dict[str, Dict[str, Any]], response["wallet_balances"])
121
144

122
145
async def get_transaction(self, wallet_id: int, transaction_id: bytes32) -> TransactionRecord:
123
-- res = await self.fetch(
124
-- "get_transaction",
125
-- {"walled_id": wallet_id, "transaction_id": transaction_id.hex()},
126
-- )
127
-- return TransactionRecord.from_json_dict_convenience(res["transaction"])
146
++ request = {"walled_id": wallet_id, "transaction_id": transaction_id.hex()}
147
++ response = await self.fetch("get_transaction", request)
148
++ return TransactionRecord.from_json_dict_convenience(response["transaction"])
128
149

129
150
async def get_transactions(
130
151
self,
131
152
wallet_id: int,
132
-- start: int = None,
133
-- end: int = None,
134
-- sort_key: SortKey = None,
153
++ start: Optional[int] = None,
154
++ end: Optional[int] = None,
155
++ sort_key: Optional[SortKey] = None,
135
156
reverse: bool = False,
136
157
to_address: Optional[str] = None,
137
158
type_filter: Optional[TransactionTypeFilter] = None,
@@@ -156,29 -156,29 +177,26 @@@
156
177
if confirmed is not None:
157
178
request["confirmed"] = confirmed
158
179

159
-- res = await self.fetch(
160
-- "get_transactions",
161
-- request,
162
-- )
180
++ res = await self.fetch("get_transactions", request)
163
181
return [TransactionRecord.from_json_dict_convenience(tx) for tx in res["transactions"]]
164
182

165
183
async def get_transaction_count(
166
-- self,
167
-- wallet_id: int,
168
-- confirmed: Optional[bool] = None,
169
-- type_filter: Optional[TransactionTypeFilter] = None,
170
-- ) -> List[TransactionRecord]:
184
++ self, wallet_id: int, confirmed: Optional[bool] = None, type_filter: Optional[TransactionTypeFilter] = None
185
++ ) -> int:
171
186
request: Dict[str, Any] = {"wallet_id": wallet_id}
172
187
if type_filter is not None:
173
188
request["type_filter"] = type_filter.to_json_dict()
174
--
175
189
if confirmed is not None:
176
190
request["confirmed"] = confirmed
177
191
res = await self.fetch("get_transaction_count", request)
178
-- return res["count"]
192
++ # TODO: casting due to lack of type checked deserialization
193
++ return cast(int, res["count"])
179
194

180
195
async def get_next_address(self, wallet_id: int, new_address: bool) -> str:
181
-- return (await self.fetch("get_next_address", {"wallet_id": wallet_id, "new_address": new_address}))["address"]
196
++ request = {"wallet_id": wallet_id, "new_address": new_address}
197
++ response = await self.fetch("get_next_address", request)
198
++ # TODO: casting due to lack of type checked deserialization
199
++ return cast(str, response["address"])
182
200

183
201
async def send_transaction(
184
202
self,
@@@ -212,9 -212,9 +230,9 @@@
212
230
async def send_transaction_multi(
213
231
self,
214
232
wallet_id: int,
215
-- additions: List[Dict],
233
++ additions: List[Dict[str, Any]],
216
234
tx_config: TXConfig,
217
-- coins: List[Coin] = None,
235
++ coins: Optional[List[Coin]] = None,
218
236
fee: uint64 = uint64(0),
219
237
push: bool = True,
220
238
) -> TransactionRecord:
@@@ -244,40 -244,40 +262,38 @@@
244
262
force: bool = False,
245
263
extra_conditions: Tuple[Condition, ...] = tuple(),
246
264
timelock_info: ConditionValidTimes = ConditionValidTimes(),
247
-- ) -> Dict:
248
-- response = await self.fetch(
249
-- "spend_clawback_coins",
250
-- {
251
-- "coin_ids": [cid.hex() for cid in coin_ids],
252
-- "fee": fee,
253
-- "force": force,
254
-- "extra_conditions": conditions_to_json_dicts(extra_conditions),
255
-- **timelock_info.to_json_dict(),
256
-- },
257
-- )
265
++ ) -> Dict[str, Any]:
266
++ request = {
267
++ "coin_ids": [cid.hex() for cid in coin_ids],
268
++ "fee": fee,
269
++ "force": force,
270
++ "extra_conditions": conditions_to_json_dicts(extra_conditions),
271
++ **timelock_info.to_json_dict(),
272
++ }
273
++ response = await self.fetch("spend_clawback_coins", request)
258
274
return response
259
275

260
276
async def delete_unconfirmed_transactions(self, wallet_id: int) -> None:
261
-- await self.fetch(
262
-- "delete_unconfirmed_transactions",
263
-- {"wallet_id": wallet_id},
264
-- )
265
-- return None
277
++ await self.fetch("delete_unconfirmed_transactions", {"wallet_id": wallet_id})
266
278

267
279
async def get_current_derivation_index(self) -> str:
268
-- return (await self.fetch("get_current_derivation_index", {}))["index"]
280
++ response = await self.fetch("get_current_derivation_index", {})
281
++ index = response["index"]
282
++ return str(index)
269
283

270
284
async def extend_derivation_index(self, index: int) -> str:
271
-- return (await self.fetch("extend_derivation_index", {"index": index}))["index"]
285
++ response = await self.fetch("extend_derivation_index", {"index": index})
286
++ updated_index = response["index"]
287
++ return str(updated_index)
272
288

273
-- async def get_farmed_amount(self) -> Dict:
289
++ async def get_farmed_amount(self) -> Dict[str, Any]:
274
290
return await self.fetch("get_farmed_amount", {})
275
291

276
292
async def create_signed_transactions(
277
293
self,
278
-- additions: List[Dict],
294
++ additions: List[Dict[str, Any]],
279
295
tx_config: TXConfig,
280
-- coins: List[Coin] = None,
296
++ coins: Optional[List[Coin]] = None,
281
297
fee: uint64 = uint64(0),
282
298
wallet_id: Optional[int] = None,
283
299
extra_conditions: Tuple[Condition, ...] = tuple(),
@@@ -291,7 -291,7 +307,7 @@@
291
307
if "memos" in ad:
292
308
additions_hex[-1]["memos"] = ad["memos"]
293
309

294
-- request: Dict[str, Any] = {
310
++ request = {
295
311
"additions": additions_hex,
296
312
"fee": fee,
297
313
"extra_conditions": conditions_to_json_dicts(extra_conditions),
@@@ -307,14 -307,14 +323,14 @@@
307
323
if wallet_id:
308
324
request["wallet_id"] = wallet_id
309
325

310
-- response: Dict = await self.fetch("create_signed_transaction", request)
326
++ response = await self.fetch("create_signed_transaction", request)
311
327
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["signed_txs"]]
312
328

313
329
async def create_signed_transaction(
314
330
self,
315
-- additions: List[Dict],
331
++ additions: List[Dict[str, Any]],
316
332
tx_config: TXConfig,
317
-- coins: List[Coin] = None,
333
++ coins: Optional[List[Coin]] = None,
318
334
fee: uint64 = uint64(0),
319
335
wallet_id: Optional[int] = None,
320
336
push: bool = False,
@@@ -336,36 -336,36 +352,22 @@@
336
352

337
353
return txs[0]
338
354

339
-- async def select_coins(
340
-- self,
341
-- amount: int,
342
-- wallet_id: int,
343
-- coin_selection_config: CoinSelectionConfig,
344
-- ) -> List[Coin]:
345
-- request = {
346
-- "amount": amount,
347
-- "wallet_id": wallet_id,
348
-- **coin_selection_config.to_json_dict(),
349
-- }
350
-- response: Dict[str, List[Dict]] = await self.fetch("select_coins", request)
355
++ async def select_coins(self, amount: int, wallet_id: int, coin_selection_config: CoinSelectionConfig) -> List[Coin]:
356
++ request = {"amount": amount, "wallet_id": wallet_id, **coin_selection_config.to_json_dict()}
357
++ response = await self.fetch("select_coins", request)
351
358
return [Coin.from_json_dict(coin) for coin in response["coins"]]
352
359

353
360
async def get_coin_records(self, request: GetCoinRecords) -> Dict[str, Any]:
354
361
return await self.fetch("get_coin_records", request.to_json_dict())
355
362

356
363
async def get_spendable_coins(
357
-- self,
358
-- wallet_id: int,
359
-- coin_selection_config: CoinSelectionConfig,
364
++ self, wallet_id: int, coin_selection_config: CoinSelectionConfig
360
365
) -> Tuple[List[CoinRecord], List[CoinRecord], List[Coin]]:
361
366
"""
362
367
We return a tuple containing: (confirmed records, unconfirmed removals, unconfirmed additions)
363
368
"""
364
-- request = {
365
-- "wallet_id": wallet_id,
366
-- **coin_selection_config.to_json_dict(),
367
-- }
368
-- response: Dict[str, List[Dict]] = await self.fetch("get_spendable_coins", request)
369
++ request = {"wallet_id": wallet_id, **coin_selection_config.to_json_dict()}
370
++ response = await self.fetch("get_spendable_coins", request)
369
371
confirmed_wrs = [CoinRecord.from_json_dict(coin) for coin in response["confirmed_records"]]
370
372
unconfirmed_removals = [CoinRecord.from_json_dict(coin) for coin in response["unconfirmed_removals"]]
371
373
unconfirmed_additions = [Coin.from_json_dict(coin) for coin in response["unconfirmed_additions"]]
@@@ -377,7 -377,7 +379,7 @@@
377
379
include_spent_coins: bool = True,
378
380
start_height: Optional[int] = None,
379
381
end_height: Optional[int] = None,
380
-- ) -> List:
382
++ ) -> List[CoinRecord]:
381
383
names_hex = [name.hex() for name in names]
382
384
request = {"names": names_hex, "include_spent_coins": include_spent_coins}
383
385
if start_height is not None:
@@@ -396,8 -396,8 +398,8 @@@
396
398
name: Optional[str] = "DID Wallet",
397
399
backup_ids: List[str] = [],
398
400
required_num: int = 0,
399
-- ) -> Dict:
400
-- request: Dict[str, Any] = {
401
++ ) -> Dict[str, Any]:
402
++ request = {
401
403
"wallet_type": "did_wallet",
402
404
"did_type": "new",
403
405
"backup_dids": backup_ids,
@@@ -409,26 -409,26 +411,18 @@@
409
411
response = await self.fetch("create_new_wallet", request)
410
412
return response
411
413

412
-- async def get_did_id(self, wallet_id: int) -> Dict:
413
-- request: Dict[str, Any] = {
414
-- "wallet_id": wallet_id,
415
-- }
414
++ async def get_did_id(self, wallet_id: int) -> Dict[str, Any]:
415
++ request = {"wallet_id": wallet_id}
416
416
response = await self.fetch("did_get_did", request)
417
417
return response
418
418

419
-- async def get_did_info(self, coin_id: str, latest: bool) -> Dict:
420
-- request: Dict[str, Any] = {
421
-- "coin_id": coin_id,
422
-- "latest": latest,
423
-- }
419
++ async def get_did_info(self, coin_id: str, latest: bool) -> Dict[str, Any]:
420
++ request = {"coin_id": coin_id, "latest": latest}
424
421
response = await self.fetch("did_get_info", request)
425
422
return response
426
423

427
-- async def create_did_backup_file(self, wallet_id: int, filename: str) -> Dict:
428
-- request: Dict[str, Any] = {
429
-- "wallet_id": wallet_id,
430
-- "filename": filename,
431
-- }
424
++ async def create_did_backup_file(self, wallet_id: int, filename: str) -> Dict[str, Any]:
425
++ request = {"wallet_id": wallet_id, "filename": filename}
432
426
response = await self.fetch("did_create_backup_file", request)
433
427
return response
434
428

@@@ -441,7 -441,7 +435,7 @@@
441
435
extra_conditions: Tuple[Condition, ...] = tuple(),
442
436
timelock_info: ConditionValidTimes = ConditionValidTimes(),
443
437
push: bool = True,
444
-- ) -> Dict:
438
++ ) -> Dict[str, Any]:
445
439
request: Dict[str, Any] = {
446
440
"wallet_id": wallet_id,
447
441
"new_list": recovery_list,
@@@ -454,10 -454,10 +448,8 @@@
454
448
response = await self.fetch("did_update_recovery_ids", request)
455
449
return response
456
450

457
-- async def get_did_recovery_list(self, wallet_id: int) -> Dict:
458
-- request: Dict[str, Any] = {
459
-- "wallet_id": wallet_id,
460
-- }
451
++ async def get_did_recovery_list(self, wallet_id: int) -> Dict[str, Any]:
452
++ request = {"wallet_id": wallet_id}
461
453
response = await self.fetch("did_get_recovery_list", request)
462
454
return response
463
455

@@@ -468,7 -468,7 +460,7 @@@
468
460
extra_conditions: Tuple[Condition, ...] = tuple(),
469
461
timelock_info: ConditionValidTimes = ConditionValidTimes(),
470
462
push: bool = False,
471
-- ) -> Dict:
463
++ ) -> Dict[str, Any]:
472
464
request: Dict[str, Any] = {
473
465
"wallet_id": wallet_id,
474
466
"extra_conditions": conditions_to_json_dicts(extra_conditions),
@@@ -482,12 -482,12 +474,12 @@@
482
474
async def update_did_metadata(
483
475
self,
484
476
wallet_id: int,
485
-- metadata: Dict,
477
++ metadata: Dict[str, Any],
486
478
tx_config: TXConfig,
487
479
extra_conditions: Tuple[Condition, ...] = tuple(),
488
480
timelock_info: ConditionValidTimes = ConditionValidTimes(),
489
481
push: bool = True,
490
-- ) -> Dict:
482
++ ) -> Dict[str, Any]:
491
483
request: Dict[str, Any] = {
492
484
"wallet_id": wallet_id,
493
485
"metadata": metadata,
@@@ -499,19 -499,19 +491,19 @@@
499
491
response = await self.fetch("did_update_metadata", request)
500
492
return response
501
493

502
-- async def get_did_metadata(self, wallet_id: int) -> Dict:
503
-- request: Dict[str, Any] = {
504
-- "wallet_id": wallet_id,
505
-- }
494
++ async def get_did_metadata(self, wallet_id: int) -> Dict[str, Any]:
495
++ request = {"wallet_id": wallet_id}
506
496
response = await self.fetch("did_get_metadata", request)
507
497
return response
508
498

509
499
async def find_lost_did(
510
-- self, coin_id: str, recovery_list_hash: Optional[str], metadata: Optional[Dict], num_verification: Optional[int]
511
-- ) -> Dict:
512
-- request: Dict[str, Any] = {
513
-- "coin_id": coin_id,
514
-- }
500
++ self,
501
++ coin_id: str,
502
++ recovery_list_hash: Optional[str],
503
++ metadata: Optional[Dict[str, Any]],
504
++ num_verification: Optional[int],
505
++ ) -> Dict[str, Any]:
506
++ request: Dict[str, Any] = {"coin_id": coin_id}
515
507
if recovery_list_hash is not None:
516
508
request["recovery_list_hash"] = recovery_list_hash
517
509
if metadata is not None:
@@@ -521,12 -521,12 +513,8 @@@
521
513
response = await self.fetch("did_find_lost_did", request)
522
514
return response
523
515

524
-- async def create_new_did_wallet_from_recovery(self, filename: str) -> Dict:
525
-- request: Dict[str, Any] = {
526
-- "wallet_type": "did_wallet",
527
-- "did_type": "recovery",
528
-- "filename": filename,
529
-- }
516
++ async def create_new_did_wallet_from_recovery(self, filename: str) -> Dict[str, Any]:
517
++ request = {"wallet_type": "did_wallet", "did_type": "recovery", "filename": filename}
530
518
response = await self.fetch("create_new_wallet", request)
531
519
return response
532
520

@@@ -539,8 -539,8 +527,8 @@@
539
527
file_name: str,
540
528
extra_conditions: Tuple[Condition, ...] = tuple(),
541
529
timelock_info: ConditionValidTimes = ConditionValidTimes(),
542
-- ) -> Dict:
543
-- request: Dict[str, Any] = {
530
++ ) -> Dict[str, Any]:
531
++ request = {
544
532
"wallet_id": wallet_id,
545
533
"coin_name": coin_name,
546
534
"pubkey": pubkey,
@@@ -552,11 -552,11 +540,8 @@@
552
540
response = await self.fetch("did_create_attest", request)
553
541
return response
554
542

555
-- async def did_recovery_spend(self, wallet_id: int, attest_filenames: str) -> Dict:
556
-- request: Dict[str, Any] = {
557
-- "wallet_id": wallet_id,
558
-- "attest_filenames": attest_filenames,
559
-- }
543
++ async def did_recovery_spend(self, wallet_id: int, attest_filenames: str) -> Dict[str, Any]:
544
++ request = {"wallet_id": wallet_id, "attest_filenames": attest_filenames}
560
545
response = await self.fetch("did_recovery_spend", request)
561
546
return response
562
547

@@@ -570,7 -570,7 +555,7 @@@
570
555
extra_conditions: Tuple[Condition, ...] = tuple(),
571
556
timelock_info: ConditionValidTimes = ConditionValidTimes(),
572
557
push: bool = True,
573
-- ) -> Dict:
558
++ ) -> Dict[str, Any]:
574
559
request: Dict[str, Any] = {
575
560
"wallet_id": wallet_id,
576
561
"inner_address": address,
@@@ -584,12 -584,12 +569,12 @@@
584
569
response = await self.fetch("did_transfer_did", request)
585
570
return response
586
571

587
-- async def did_set_wallet_name(self, wallet_id: int, name: str) -> Dict:
572
++ async def did_set_wallet_name(self, wallet_id: int, name: str) -> Dict[str, Any]:
588
573
request = {"wallet_id": wallet_id, "name": name}
589
574
response = await self.fetch("did_set_wallet_name", request)
590
575
return response
591
576

592
-- async def did_get_wallet_name(self, wallet_id: int) -> Dict:
577
++ async def did_get_wallet_name(self, wallet_id: int) -> Dict[str, Any]:
593
578
request = {"wallet_id": wallet_id}
594
579
response = await self.fetch("did_get_wallet_name", request)
595
580
return response
@@@ -609,7 -609,7 +594,7 @@@
609
594
extra_conditions: Tuple[Condition, ...] = tuple(),
610
595
timelock_info: ConditionValidTimes = ConditionValidTimes(),
611
596
) -> TransactionRecord:
612
-- request: Dict[str, Any] = {
597
++ request = {
613
598
"wallet_type": "pool_wallet",
614
599
"mode": mode,
615
600
"initial_target_state": {
@@@ -629,14 -629,14 +614,14 @@@
629
614
res = await self.fetch("create_new_wallet", request)
630
615
return TransactionRecord.from_json_dict(res["transaction"])
631
616

632
-- async def pw_self_pool(self, wallet_id: int, fee: uint64) -> Dict:
617
++ async def pw_self_pool(self, wallet_id: int, fee: uint64) -> Dict[str, Any]:
633
618
reply = await self.fetch("pw_self_pool", {"wallet_id": wallet_id, "fee": fee})
634
619
reply = parse_result_transactions(reply)
635
620
return reply
636
621

637
622
async def pw_join_pool(
638
623
self, wallet_id: int, target_puzzlehash: bytes32, pool_url: str, relative_lock_height: uint32, fee: uint64
639
-- ) -> Dict:
624
++ ) -> Dict[str, Any]:
640
625
request = {
641
626
"wallet_id": int(wallet_id),
642
627
"target_puzzlehash": target_puzzlehash.hex(),
@@@ -644,14 -644,14 +629,13 @@@
644
629
"pool_url": pool_url,
645
630
"fee": fee,
646
631
}
647
--
648
632
reply = await self.fetch("pw_join_pool", request)
649
633
reply = parse_result_transactions(reply)
650
634
return reply
651
635

652
636
async def pw_absorb_rewards(
653
637
self, wallet_id: int, fee: uint64 = uint64(0), max_spends_in_tx: Optional[int] = None
654
-- ) -> Dict:
638
++ ) -> Dict[str, Any]:
655
639
reply = await self.fetch(
656
640
"pw_absorb_rewards", {"wallet_id": wallet_id, "fee": fee, "max_spends_in_tx": max_spends_in_tx}
657
641
)
@@@ -667,38 -667,38 +651,27 @@@
667
651
)
668
652

669
653
# CATS
670
-- async def create_new_cat_and_wallet(self, amount: uint64, fee: uint64 = uint64(0), test: bool = False) -> Dict:
671
-- request: Dict[str, Any] = {
672
-- "wallet_type": "cat_wallet",
673
-- "mode": "new",
674
-- "amount": amount,
675
-- "fee": fee,
676
-- "test": test,
677
-- }
654
++ async def create_new_cat_and_wallet(
655
++ self, amount: uint64, fee: uint64 = uint64(0), test: bool = False
656
++ ) -> Dict[str, Any]:
657
++ request = {"wallet_type": "cat_wallet", "mode": "new", "amount": amount, "fee": fee, "test": test}
678
658
return await self.fetch("create_new_wallet", request)
679
659

680
-- async def create_wallet_for_existing_cat(self, asset_id: bytes) -> Dict:
681
-- request: Dict[str, Any] = {
682
-- "wallet_type": "cat_wallet",
683
-- "asset_id": asset_id.hex(),
684
-- "mode": "existing",
685
-- }
660
++ async def create_wallet_for_existing_cat(self, asset_id: bytes) -> Dict[str, Any]:
661
++ request = {"wallet_type": "cat_wallet", "asset_id": asset_id.hex(), "mode": "existing"}
686
662
return await self.fetch("create_new_wallet", request)
687
663

688
664
async def get_cat_asset_id(self, wallet_id: int) -> bytes32:
689
-- request: Dict[str, Any] = {
690
-- "wallet_id": wallet_id,
691
-- }
665
++ request = {"wallet_id": wallet_id}
692
666
return bytes32.from_hexstr((await self.fetch("cat_get_asset_id", request))["asset_id"])
693
667

694
-- async def get_stray_cats(self) -> Dict:
668
++ async def get_stray_cats(self) -> List[Dict[str, Any]]:
695
669
response = await self.fetch("get_stray_cats", {})
696
-- return response["stray_cats"]
670
++ # TODO: casting due to lack of type checked deserialization
671
++ return cast(List[Dict[str, Any]], response["stray_cats"])
697
672

698
673
async def cat_asset_id_to_name(self, asset_id: bytes32) -> Optional[Tuple[Optional[uint32], str]]:
699
-- request: Dict[str, Any] = {
700
-- "asset_id": asset_id.hex(),
701
-- }
674
++ request = {"asset_id": asset_id.hex()}
702
675
try:
703
676
res = await self.fetch("cat_asset_id_to_name", request)
704
677
except ValueError:
@@@ -708,10 -708,10 +681,10 @@@
708
681
return wallet_id, res["name"]
709
682

710
683
async def get_cat_name(self, wallet_id: int) -> str:
711
-- request: Dict[str, Any] = {
712
-- "wallet_id": wallet_id,
713
-- }
714
-- return (await self.fetch("cat_get_name", request))["name"]
684
++ request = {"wallet_id": wallet_id}
685
++ response = await self.fetch("cat_get_name", request)
686
++ # TODO: casting due to lack of type checked deserialization
687
++ return cast(str, response["name"])
715
688

716
689
async def set_cat_name(self, wallet_id: int, name: str) -> None:
717
690
request: Dict[str, Any] = {
@@@ -735,10 -735,10 +708,10 @@@
735
708
timelock_info: ConditionValidTimes = ConditionValidTimes(),
736
709
push: bool = True,
737
710
) -> TransactionRecord:
738
-- send_dict: Dict[str, Any] = {
711
++ send_dict = {
739
712
"wallet_id": wallet_id,
740
713
"fee": fee,
741
-- "memos": memos if memos else [],
714
++ "memos": memos if memos is not None else [],
742
715
"extra_conditions": conditions_to_json_dicts(extra_conditions),
743
716
"push": push,
744
717
**tx_config.to_json_dict(),
@@@ -770,9 -770,9 +743,9 @@@
770
743
self,
771
744
offer_dict: Dict[Union[uint32, str], int],
772
745
tx_config: TXConfig,
773
-- driver_dict: Dict[str, Any] = None,
774
-- solver: Dict[str, Any] = None,
775
-- fee=uint64(0),
746
++ driver_dict: Optional[Dict[str, Any]] = None,
747
++ solver: Optional[Dict[str, Any]] = None,
748
++ fee: int = 0,
776
749
validate_only: bool = False,
777
750
extra_conditions: Tuple[Condition, ...] = tuple(),
778
751
timelock_info: ConditionValidTimes = ConditionValidTimes(),
@@@ -810,8 -810,8 +783,8 @@@
810
783
self,
811
784
offer: Offer,
812
785
tx_config: TXConfig,
813
-- solver: Dict[str, Any] = None,
814
-- fee=uint64(0),
786
++ solver: Optional[Dict[str, Any]] = None,
787
++ fee: int = 0,
815
788
extra_conditions: Tuple[Condition, ...] = tuple(),
816
789
timelock_info: ConditionValidTimes = ConditionValidTimes(),
817
790
push: bool = True,
@@@ -838,7 -838,7 +811,7 @@@
838
811
self,
839
812
start: int = 0,
840
813
end: int = 50,
841
-- sort_key: str = None,
814
++ sort_key: Optional[str] = None,
842
815
reverse: bool = False,
843
816
file_contents: bool = False,
844
817
exclude_my_offers: bool = False,
@@@ -873,12 -873,12 +846,12 @@@
873
846
self,
874
847
trade_id: bytes32,
875
848
tx_config: TXConfig,
876
-- fee=uint64(0),
849
++ fee: int = 0,
877
850
secure: bool = True,
878
851
extra_conditions: Tuple[Condition, ...] = tuple(),
879
852
timelock_info: ConditionValidTimes = ConditionValidTimes(),
880
853
push: bool = True,
881
-- ):
854
++ ) -> None:
882
855
await self.fetch(
883
856
"cancel_offer",
884
857
{
@@@ -895,7 -895,7 +868,7 @@@
895
868
async def cancel_offers(
896
869
self,
897
870
tx_config: TXConfig,
898
-- fee=uint64(0),
871
++ batch_fee: int = 0,
899
872
secure: bool = True,
900
873
batch_size: int = 5,
901
874
cancel_all: bool = False,
@@@ -908,7 -908,7 +881,7 @@@
908
881
"cancel_offers",
909
882
{
910
883
"secure": secure,
911
-- "batch_fee": fee,
884
++ "batch_fee": batch_fee,
912
885
"secure": secure,
913
886
"batch_size": batch_size,
914
887
"cancel_all": cancel_all,
@@@ -921,36 -921,36 +894,32 @@@
921
894
)
922
895

923
896
# NFT wallet
924
-- async def create_new_nft_wallet(self, did_id, name=None) -> dict[str, Any]:
925
-- request: Dict[str, Any] = {
926
-- "wallet_type": "nft_wallet",
927
-- "did_id": did_id,
928
-- "name": name,
929
-- }
897
++ async def create_new_nft_wallet(self, did_id: Optional[str], name: Optional[str] = None) -> Dict[str, Any]:
898
++ request = {"wallet_type": "nft_wallet", "did_id": did_id, "name": name}
930
899
response = await self.fetch("create_new_wallet", request)
931
900
return response
932
901

933
902
async def mint_nft(
934
903
self,
935
-- wallet_id,
936
-- royalty_address,
937
-- target_address,
938
-- hash,
939
-- uris,
904
++ wallet_id: int,
905
++ royalty_address: Optional[str],
906
++ target_address: Optional[str],
907
++ hash: str,
908
++ uris: List[str],
940
909
tx_config: TXConfig,
941
-- meta_hash="",
942
-- meta_uris=[],
943
-- license_hash="",
944
-- license_uris=[],
945
-- edition_total=1,
946
-- edition_number=1,
947
-- fee=0,
948
-- royalty_percentage=0,
949
-- did_id=None,
910
++ meta_hash: Optional[str] = "",
911
++ meta_uris: List[str] = [],
912
++ license_hash: Optional[str] = "",
913
++ license_uris: List[str] = [],
914
++ edition_total: Optional[int] = 1,
915
++ edition_number: Optional[int] = 1,
916
++ fee: int = 0,
917
++ royalty_percentage: int = 0,
918
++ did_id: Optional[str] = None,
950
919
extra_conditions: Tuple[Condition, ...] = tuple(),
951
920
timelock_info: ConditionValidTimes = ConditionValidTimes(),
952
921
push: bool = True,
953
-- ):
922
++ ) -> Dict[str, Any]:
954
923
request: Dict[str, Any] = {
955
924
"wallet_id": wallet_id,
956
925
"royalty_address": royalty_address,
@@@ -976,16 -976,16 +945,16 @@@
976
945

977
946
async def add_uri_to_nft(
978
947
self,
979
-- wallet_id,
980
-- nft_coin_id,
981
-- key,
982
-- uri,
983
-- fee,
948
++ wallet_id: int,
949
++ nft_coin_id: str,
950
++ key: str,
951
++ uri: str,
952
++ fee: int,
984
953
tx_config: TXConfig,
985
954
extra_conditions: Tuple[Condition, ...] = tuple(),
986
955
timelock_info: ConditionValidTimes = ConditionValidTimes(),
987
956
push: bool = True,
988
-- ):
957
++ ) -> Dict[str, Any]:
989
958
request: Dict[str, Any] = {
990
959
"wallet_id": wallet_id,
991
960
"nft_coin_id": nft_coin_id,
@@@ -1004,8 -1004,8 +973,8 @@@
1004
973
self,
1005
974
royalty_assets_dict: Dict[Any, Tuple[Any, uint16]],
1006
975
fungible_asset_dict: Dict[Any, uint64],
1007
-- ) -> Dict[Any, List[Dict[str, Any]]]:
1008
-- request: Dict[str, Any] = {
976
++ ) -> Dict[str, List[Dict[str, Any]]]:
977
++ request = {
1009
978
"royalty_assets": [
1010
979
{"asset": id, "royalty_address": royalty_info[0], "royalty_percentage": royalty_info[1]}
1011
980
for id, royalty_info in royalty_assets_dict.items()
@@@ -1016,22 -1016,22 +985,22 @@@
1016
985
del response["success"]
1017
986
return response
1018
987

1019
-- async def get_nft_info(self, coin_id: str, latest: bool = True):
1020
-- request: Dict[str, Any] = {"coin_id": coin_id, "latest": latest}
988
++ async def get_nft_info(self, coin_id: str, latest: bool = True) -> Dict[str, Any]:
989
++ request = {"coin_id": coin_id, "latest": latest}
1021
990
response = await self.fetch("nft_get_info", request)
1022
991
return response
1023
992

1024
993
async def transfer_nft(
1025
994
self,
1026
-- wallet_id,
1027
-- nft_coin_id,
1028
-- target_address,
1029
-- fee,
995
++ wallet_id: int,
996
++ nft_coin_id: str,
997
++ target_address: str,
998
++ fee: int,
1030
999
tx_config: TXConfig,
1031
1000
extra_conditions: Tuple[Condition, ...] = tuple(),
1032
1001
timelock_info: ConditionValidTimes = ConditionValidTimes(),
1033
1002
push: bool = True,
1034
-- ):
1003
++ ) -> Dict[str, Any]:
1035
1004
request: Dict[str, Any] = {
1036
1005
"wallet_id": wallet_id,
1037
1006
"nft_coin_id": nft_coin_id,
@@@ -1045,27 -1045,27 +1014,27 @@@
1045
1014
response = await self.fetch("nft_transfer_nft", request)
1046
1015
return response
1047
1016

1048
-- async def count_nfts(self, wallet_id: Optional[int]):
1049
-- request: Dict[str, Any] = {"wallet_id": wallet_id}
1017
++ async def count_nfts(self, wallet_id: Optional[int]) -> Dict[str, Any]:
1018
++ request = {"wallet_id": wallet_id}
1050
1019
response = await self.fetch("nft_count_nfts", request)
1051
1020
return response
1052
1021

1053
-- async def list_nfts(self, wallet_id, num: int = 50, start_index: int = 0):
1054
-- request: Dict[str, Any] = {"wallet_id": wallet_id, "num": num, "start_index": start_index}
1022
++ async def list_nfts(self, wallet_id: int, num: int = 50, start_index: int = 0) -> Dict[str, Any]:
1023
++ request = {"wallet_id": wallet_id, "num": num, "start_index": start_index}
1055
1024
response = await self.fetch("nft_get_nfts", request)
1056
1025
return response
1057
1026

1058
1027
async def set_nft_did(
1059
1028
self,
1060
-- wallet_id,
1061
-- did_id,
1062
-- nft_coin_id,
1063
-- fee,
1029
++ wallet_id: int,
1030
++ did_id: str,
1031
++ nft_coin_id: str,
1032
++ fee: int,
1064
1033
tx_config: TXConfig,
1065
1034
extra_conditions: Tuple[Condition, ...] = tuple(),
1066
1035
timelock_info: ConditionValidTimes = ConditionValidTimes(),
1067
1036
push: bool = True,
1068
-- ):
1037
++ ) -> Dict[str, Any]:
1069
1038
request: Dict[str, Any] = {
1070
1039
"wallet_id": wallet_id,
1071
1040
"did_id": did_id,
@@@ -1079,8 -1079,8 +1048,8 @@@
1079
1048
response = await self.fetch("nft_set_nft_did", request)
1080
1049
return response
1081
1050

1082
-- async def get_nft_wallet_did(self, wallet_id) -> dict[str, Optional[str]]:
1083
-- request: Dict[str, Any] = {"wallet_id": wallet_id}
1051
++ async def get_nft_wallet_did(self, wallet_id: int) -> Dict[str, Any]:
1052
++ request = {"wallet_id": wallet_id}
1084
1053
response = await self.fetch("nft_get_wallet_did", request)
1085
1054
return response
1086
1055

@@@ -1094,17 -1094,17 +1063,17 @@@
1094
1063
target_list: Optional[List[str]] = None,
1095
1064
mint_number_start: Optional[int] = 1,
1096
1065
mint_total: Optional[int] = None,
1097
-- xch_coins: Optional[List[Dict]] = None,
1066
++ xch_coins: Optional[List[Dict[str, Any]]] = None,
1098
1067
xch_change_target: Optional[str] = None,
1099
1068
new_innerpuzhash: Optional[str] = None,
1100
-- did_coin: Optional[Dict] = None,
1069
++ did_coin: Optional[Dict[str, Any]] = None,
1101
1070
did_lineage_parent: Optional[str] = None,
1102
1071
mint_from_did: Optional[bool] = False,
1103
1072
fee: Optional[int] = 0,
1104
1073
extra_conditions: Tuple[Condition, ...] = tuple(),
1105
1074
timelock_info: ConditionValidTimes = ConditionValidTimes(),
1106
1075
push: bool = False,
1107
-- ) -> Dict:
1076
++ ) -> Dict[str, Any]:
1108
1077
request = {
1109
1078
"wallet_id": wallet_id,
1110
1079
"metadata_list": metadata_list,
@@@ -1143,21 -1143,21 +1112,17 @@@
1143
1112
**timelock_info.to_json_dict(),
1144
1113
}
1145
1114
response = await self.fetch("create_new_dl", request)
1146
-- txs: List[TransactionRecord] = [
1147
-- TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]
1148
-- ]
1149
-- launcher_id: bytes32 = bytes32.from_hexstr(response["launcher_id"])
1115
++ txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]]
1116
++ launcher_id = bytes32.from_hexstr(response["launcher_id"])
1150
1117
return txs, launcher_id
1151
1118

1152
1119
async def dl_track_new(self, launcher_id: bytes32) -> None:
1153
1120
request = {"launcher_id": launcher_id.hex()}
1154
1121
await self.fetch("dl_track_new", request)
1155
-- return None
1156
1122

1157
1123
async def dl_stop_tracking(self, launcher_id: bytes32) -> None:
1158
1124
request = {"launcher_id": launcher_id.hex()}
1159
1125
await self.fetch("dl_stop_tracking", request)
1160
-- return None
1161
1126

1162
1127
async def dl_latest_singleton(
1163
1128
self, launcher_id: bytes32, only_confirmed: bool = False
@@@ -1195,9 -1195,9 +1160,7 @@@
1195
1160
extra_conditions: Tuple[Condition, ...] = tuple(),
1196
1161
timelock_info: ConditionValidTimes = ConditionValidTimes(),
1197
1162
) -> List[TransactionRecord]:
1198
-- updates_as_strings: Dict[str, str] = {}
1199
-- for lid, root in update_dictionary.items():
1200
-- updates_as_strings[str(lid)] = str(root)
1163
++ updates_as_strings = {str(lid): str(root) for lid, root in update_dictionary.items()}
1201
1164
request = {
1202
1165
"updates": updates_as_strings,
1203
1166
"extra_conditions": conditions_to_json_dicts(extra_conditions),
@@@ -1296,11 -1296,11 +1259,13 @@@
1296
1259
]
1297
1260

1298
1261
async def delete_notifications(self, ids: Optional[List[bytes32]] = None) -> bool:
1299
-- request: Dict[str, Any] = {}
1262
++ request = {}
1300
1263
if ids is not None:
1301
1264
request["ids"] = [id.hex() for id in ids]
1302
1265
response = await self.fetch("delete_notifications", request)
1303
-- return response["success"]
1266
++ # TODO: casting due to lack of type checked deserialization
1267
++ result = cast(bool, response["success"])
1268
++ return result
1304
1269

1305
1270
async def send_notification(
1306
1271
self,
@@@ -1345,8 -1345,8 +1310,8 @@@
1345
1310
fee: uint64 = uint64(0),
1346
1311
fee_for_cat: uint64 = uint64(0),
1347
1312
extra_conditions: Tuple[Condition, ...] = tuple(),
1348
-- ) -> Dict:
1349
-- request: Dict[str, Any] = {
1313
++ ) -> Dict[str, Any]:
1314
++ request = {
1350
1315
"wallet_type": "dao_wallet",
1351
1316
"mode": mode,
1352
1317
"treasury_id": treasury_id,
@@@ -1362,19 -1362,19 +1327,13 @@@
1362
1327
response = await self.fetch("create_new_wallet", request)
1363
1328
return response
1364
1329

1365
-- async def dao_get_treasury_id(
1366
-- self,
1367
-- wallet_id: int,
1368
-- ) -> Dict:
1369
-- request: Dict[str, Any] = {"wallet_id": wallet_id}
1330
++ async def dao_get_treasury_id(self, wallet_id: int) -> Dict[str, Any]:
1331
++ request = {"wallet_id": wallet_id}
1370
1332
response = await self.fetch("dao_get_treasury_id", request)
1371
1333
return response
1372
1334

1373
-- async def dao_get_rules(
1374
-- self,
1375
-- wallet_id: int,
1376
-- ) -> Dict:
1377
-- request: Dict[str, Any] = {"wallet_id": wallet_id}
1335
++ async def dao_get_rules(self, wallet_id: int) -> Dict[str, Any]:
1336
++ request = {"wallet_id": wallet_id}
1378
1337
response = await self.fetch("dao_get_rules", request)
1379
1338
return response
1380
1339

@@@ -1383,7 -1383,7 +1342,7 @@@
1383
1342
wallet_id: int,
1384
1343
proposal_type: str,
1385
1344
tx_config: TXConfig,
1386
-- additions: Optional[List[Dict]] = None,
1345
++ additions: Optional[List[Dict[str, Any]]] = None,
1387
1346
amount: Optional[uint64] = None,
1388
1347
inner_address: Optional[str] = None,
1389
1348
asset_id: Optional[str] = None,
@@@ -1392,8 -1392,8 +1351,8 @@@
1392
1351
new_dao_rules: Optional[Dict[str, Optional[uint64]]] = None,
1393
1352
fee: uint64 = uint64(0),
1394
1353
extra_conditions: Tuple[Condition, ...] = tuple(),
1395
-- ) -> Dict:
1396
-- request: Dict[str, Any] = {
1354
++ ) -> Dict[str, Any]:
1355
++ request = {
1397
1356
"wallet_id": wallet_id,
1398
1357
"proposal_type": proposal_type,
1399
1358
"additions": additions,
@@@ -1411,13 -1411,13 +1370,13 @@@
1411
1370
response = await self.fetch("dao_create_proposal", request)
1412
1371
return response
1413
1372

1414
-- async def dao_get_proposal_state(self, wallet_id: int, proposal_id: str):
1415
-- request: Dict[str, Any] = {"wallet_id": wallet_id, "proposal_id": proposal_id}
1373
++ async def dao_get_proposal_state(self, wallet_id: int, proposal_id: str) -> Dict[str, Any]:
1374
++ request = {"wallet_id": wallet_id, "proposal_id": proposal_id}
1416
1375
response = await self.fetch("dao_get_proposal_state", request)
1417
1376
return response
1418
1377

1419
-- async def dao_parse_proposal(self, wallet_id: int, proposal_id: str):
1420
-- request: Dict[str, Any] = {"wallet_id": wallet_id, "proposal_id": proposal_id}
1378
++ async def dao_parse_proposal(self, wallet_id: int, proposal_id: str) -> Dict[str, Any]:
1379
++ request = {"wallet_id": wallet_id, "proposal_id": proposal_id}
1421
1380
response = await self.fetch("dao_parse_proposal", request)
1422
1381
return response
1423
1382

@@@ -1430,8 -1430,8 +1389,8 @@@
1430
1389
is_yes_vote: bool = True,
1431
1390
fee: uint64 = uint64(0),
1432
1391
extra_conditions: Tuple[Condition, ...] = tuple(),
1433
-- ):
1434
-- request: Dict[str, Any] = {
1392
++ ) -> Dict[str, Any]:
1393
++ request = {
1435
1394
"wallet_id": wallet_id,
1436
1395
"proposal_id": proposal_id,
1437
1396
"vote_amount": vote_amount,
@@@ -1443,8 -1443,8 +1402,8 @@@
1443
1402
response = await self.fetch("dao_vote_on_proposal", request)
1444
1403
return response
1445
1404

1446
-- async def dao_get_proposals(self, wallet_id: int, include_closed: bool = True):
1447
-- request: Dict[str, Any] = {"wallet_id": wallet_id, "include_closed": include_closed}
1405
++ async def dao_get_proposals(self, wallet_id: int, include_closed: bool = True) -> Dict[str, Any]:
1406
++ request = {"wallet_id": wallet_id, "include_closed": include_closed}
1448
1407
response = await self.fetch("dao_get_proposals", request)
1449
1408
return response
1450
1409

@@@ -1453,11 -1453,11 +1412,11 @@@
1453
1412
wallet_id: int,
1454
1413
proposal_id: str,
1455
1414
tx_config: TXConfig,
1456
-- self_destruct: bool = None,
1415
++ self_destruct: Optional[bool] = None,
1457
1416
fee: uint64 = uint64(0),
1458
1417
extra_conditions: Tuple[Condition, ...] = tuple(),
1459
-- ):
1460
-- request: Dict[str, Any] = {
1418
++ ) -> Dict[str, Any]:
1419
++ request = {
1461
1420
"wallet_id": wallet_id,
1462
1421
"proposal_id": proposal_id,
1463
1422
"self_destruct": self_destruct,
@@@ -1474,8 -1474,8 +1433,8 @@@
1474
1433
tx_config: TXConfig,
1475
1434
fee: uint64 = uint64(0),
1476
1435
extra_conditions: Tuple[Condition, ...] = tuple(),
1477
-- ):
1478
-- request: Dict[str, Any] = {
1436
++ ) -> Dict[str, Any]:
1437
++ request = {
1479
1438
"wallet_id": wallet_id,
1480
1439
"fee": fee,
1481
1440
"extra_conditions": list(extra_conditions),
@@@ -1484,8 -1484,8 +1443,8 @@@
1484
1443
response = await self.fetch("dao_free_coins_from_finished_proposals", request)
1485
1444
return response
1486
1445

1487
-- async def dao_get_treasury_balance(self, wallet_id: int):
1488
-- request: Dict[str, Any] = {"wallet_id": wallet_id}
1446
++ async def dao_get_treasury_balance(self, wallet_id: int) -> Dict[str, Any]:
1447
++ request = {"wallet_id": wallet_id}
1489
1448
response = await self.fetch("dao_get_treasury_balance", request)
1490
1449
return response
1491
1450

@@@ -1497,8 -1497,8 +1456,8 @@@
1497
1456
tx_config: TXConfig,
1498
1457
fee: uint64 = uint64(0),
1499
1458
extra_conditions: Tuple[Condition, ...] = tuple(),
1500
-- ):
1501
-- request: Dict[str, Any] = {
1459
++ ) -> Dict[str, Any]:
1460
++ request = {
1502
1461
"wallet_id": wallet_id,
1503
1462
"funding_wallet_id": funding_wallet_id,
1504
1463
"amount": amount,
@@@ -1516,8 -1516,8 +1475,8 @@@
1516
1475
tx_config: TXConfig,
1517
1476
fee: uint64 = uint64(0),
1518
1477
extra_conditions: Tuple[Condition, ...] = tuple(),
1519
-- ):
1520
-- request: Dict[str, Any] = {
1478
++ ) -> Dict[str, Any]:
1479
++ request = {
1521
1480
"wallet_id": wallet_id,
1522
1481
"amount": amount,
1523
1482
"fee": fee,
@@@ -1531,11 -1531,11 +1490,11 @@@
1531
1490
self,
1532
1491
wallet_id: int,
1533
1492
tx_config: TXConfig,
1534
-- coins: Optional[List[Dict]] = None,
1493
++ coins: Optional[List[Dict[str, Any]]] = None,
1535
1494
fee: uint64 = uint64(0),
1536
1495
extra_conditions: Tuple[Condition, ...] = tuple(),
1537
-- ):
1538
-- request: Dict[str, Any] = {
1496
++ ) -> Dict[str, Any]:
1497
++ request = {
1539
1498
"wallet_id": wallet_id,
1540
1499
"coins": coins,
1541
1500
"fee": fee,
@@@ -1545,8 -1545,8 +1504,8 @@@
1545
1504
response = await self.fetch("dao_exit_lockup", request)
1546
1505
return response
1547
1506

1548
-- async def dao_adjust_filter_level(self, wallet_id: int, filter_level: int):
1549
-- request: Dict[str, Any] = {"wallet_id": wallet_id, "filter_level": filter_level}
1507
++ async def dao_adjust_filter_level(self, wallet_id: int, filter_level: int) -> Dict[str, Any]:
1508
++ request = {"wallet_id": wallet_id, "filter_level": filter_level}
1550
1509
response = await self.fetch("dao_adjust_filter_level", request)
1551
1510
return response
1552
1511

@@@ -1619,7 -1619,7 +1578,7 @@@
1619
1578

1620
1579
async def vc_get_proofs_for_root(self, root: bytes32) -> Dict[str, Any]:
1621
1580
response = await self.fetch("vc_get_proofs_for_root", {"root": root.hex()})
1622
-- return response["proofs"]
1581
++ return cast(Dict[str, Any], response["proofs"])
1623
1582

1624
1583
async def vc_revoke(
1625
1584
self,
chia/simulator/block_tools.py CHANGED
@@@ -981,8 -981,8 +981,8 @@@ class BlockTools
981
981
pending_ses = True
982
982
ses_hash: Optional[bytes32] = sub_epoch_summary.get_hash()
983
983
# if the last block is the last block of the epoch, we set the new sub-slot iters and difficulty
984
-- new_sub_slot_iters: Optional[uint64] = sub_epoch_summary.new_sub_slot_iters
985
-- new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty
984
++ new_sub_slot_iters: Optional[uint64] = uint64.construct_optional(sub_epoch_summary.new_sub_slot_iters)
985
++ new_difficulty: Optional[uint64] = uint64.construct_optional(sub_epoch_summary.new_difficulty)
986
986

987
987
self.log.info(f"Sub epoch summary: {sub_epoch_summary} for block {latest_block.height+1}")
988
988
else: # the previous block is not the last block of the sub-epoch or epoch
@@@ -1252,8 -1252,8 +1252,8 @@@
1252
1252
num_empty_slots_added += 1
1253
1253

1254
1254
if new_sub_slot_iters is not None and new_difficulty is not None: # new epoch
1255
-- sub_slot_iters = new_sub_slot_iters
1256
-- difficulty = new_difficulty
1255
++ sub_slot_iters = uint64(new_sub_slot_iters)
1256
++ difficulty = uint64(new_difficulty)
1257
1257

1258
1258
def create_genesis_block(
1259
1259
self,
@@@ -1350,7 -1350,7 +1350,7 @@@
1350
1350
cc_challenge,
1351
1351
ip_iters,
1352
1352
)
1353
-- cc_ip_vdf = replace(cc_ip_vdf, number_of_iterations=ip_iters)
1353
++ cc_ip_vdf = cc_ip_vdf.replace(number_of_iterations=ip_iters)
1354
1354
rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof(
1355
1355
constants,
1356
1356
ClassgroupElement.get_default_element(),
@@@ -1421,7 -1421,7 +1421,7 @@@
1421
1421
+ calculate_sp_iters(
1422
1422
self.constants,
1423
1423
self.constants.SUB_SLOT_ITERS_STARTING,
1424
-- unfinished_block.reward_chain_block.signage_point_index,
1424
++ uint8(unfinished_block.reward_chain_block.signage_point_index),
1425
1425
)
1426
1426
)
1427
1427
return unfinished_block_to_full_block(
@@@ -1552,7 -1552,7 +1552,7 @@@ def get_signage_point
1552
1552
rc_vdf_challenge,
1553
1553
rc_vdf_iters,
1554
1554
)
1555
-- cc_sp_vdf = replace(cc_sp_vdf, number_of_iterations=sp_iters)
1555
++ cc_sp_vdf = cc_sp_vdf.replace(number_of_iterations=sp_iters)
1556
1556
if normalized_to_identity_cc_sp:
1557
1557
_, cc_sp_proof = get_vdf_info_and_proof(
1558
1558
constants,
@@@ -1597,7 -1597,7 +1597,7 @@@ def finish_block
1597
1597
cc_vdf_challenge,
1598
1598
new_ip_iters,
1599
1599
)
1600
-- cc_ip_vdf = replace(cc_ip_vdf, number_of_iterations=ip_iters)
1600
++ cc_ip_vdf = cc_ip_vdf.replace(number_of_iterations=ip_iters)
1601
1601
if normalized_to_identity_cc_ip:
1602
1602
_, cc_ip_proof = get_vdf_info_and_proof(
1603
1603
constants,
@@@ -1750,7 -1750,7 +1750,7 @@@ def get_icc
1750
1750
if len(finished_sub_slots) == 0:
1751
1751
prev_deficit = latest_block.deficit
1752
1752
else:
1753
-- prev_deficit = finished_sub_slots[-1].reward_chain.deficit
1753
++ prev_deficit = uint8(finished_sub_slots[-1].reward_chain.deficit)
1754
1754

1755
1755
if deficit == prev_deficit == constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK:
1756
1756
# new slot / overflow sb to new slot / overflow sb
@@@ -2051,7 -2051,7 +2051,7 @@@ def create_block_tools
2051
2051

2052
2052

2053
2053
def make_unfinished_block(block: FullBlock, constants: ConsensusConstants) -> UnfinishedBlock:
2054
-- if is_overflow_block(constants, block.reward_chain_block.signage_point_index):
2054
++ if is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index)):
2055
2055
finished_ss = block.finished_sub_slots[:-1]
2056
2056
else:
2057
2057
finished_ss = block.finished_sub_slots
chia/timelord/iters_from_block.py CHANGED
@@@ -7,7 -7,7 +7,7 @@@ from chia.consensus.pot_iterations impo
7
7
from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
8
8
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock, RewardChainBlockUnfinished
9
9
from chia.types.blockchain_format.sized_bytes import bytes32
10
-- from chia.util.ints import uint32, uint64
10
++ from chia.util.ints import uint8, uint32, uint64
11
11

12
12

13
13
def iters_from_block(
@@@ -40,11 -40,11 +40,11 @@@
40
40
cc_sp,
41
41
)
42
42
return (
43
-- calculate_sp_iters(constants, sub_slot_iters, reward_chain_block.signage_point_index),
43
++ calculate_sp_iters(constants, sub_slot_iters, uint8(reward_chain_block.signage_point_index)),
44
44
calculate_ip_iters(
45
45
constants,
46
46
sub_slot_iters,
47
-- reward_chain_block.signage_point_index,
47
++ uint8(reward_chain_block.signage_point_index),
48
48
required_iters,
49
49
),
50
50
)
chia/timelord/timelord.py CHANGED
@@@ -256,7 -256,7 +256,7 @@@ class Timelord
256
256
log.warning(f"Received invalid unfinished block: {e}.")
257
257
return None
258
258
block_sp_total_iters = self.last_state.total_iters - ip_iters + block_sp_iters
259
-- if is_overflow_block(self.constants, block.reward_chain_block.signage_point_index):
259
++ if is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index)):
260
260
block_sp_total_iters -= self.last_state.get_sub_slot_iters()
261
261
found_index = -1
262
262
for index, (rc, total_iters) in enumerate(self.last_state.reward_challenge_cache):
@@@ -279,7 -279,7 +279,7 @@@
279
279
)
280
280
return None
281
281
if self.last_state.reward_challenge_cache[found_index][1] > block_sp_total_iters:
282
-- if not is_overflow_block(self.constants, block.reward_chain_block.signage_point_index):
282
++ if not is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index)):
283
283
log.error(
284
284
f"Will not infuse unfinished block {block.rc_prev}, sp total iters: {block_sp_total_iters}, "
285
285
f"because its iters are too low"
@@@ -485,13 -485,13 +485,13 @@@
485
485
rc_challenge = self.last_state.get_challenge(Chain.REWARD_CHAIN)
486
486
if rc_info.challenge != rc_challenge:
487
487
assert rc_challenge is not None
488
-- log.warning(f"SP: Do not have correct challenge {rc_challenge.hex()} has {rc_info.challenge}")
488
++ log.warning(f"SP: Do not have correct challenge {rc_challenge.hex()} has {rc_info.challenge.hex()}")
489
489
# This proof is on an outdated challenge, so don't use it
490
490
continue
491
491
iters_from_sub_slot_start = uint64(cc_info.number_of_iterations + self.last_state.get_last_ip())
492
492
response = timelord_protocol.NewSignagePointVDF(
493
493
signage_point_index,
494
-- dataclasses.replace(cc_info, number_of_iterations=iters_from_sub_slot_start),
494
++ cc_info.replace(number_of_iterations=iters_from_sub_slot_start),
495
495
cc_proof,
496
496
rc_info,
497
497
rc_proof,
@@@ -584,7 -584,7 +584,7 @@@
584
584
assert rc_challenge is not None
585
585
log.warning(
586
586
f"Do not have correct challenge {rc_challenge.hex()} "
587
-- f"has {rc_info.challenge}, partial hash {block.reward_chain_block.get_hash()}"
587
++ f"has {rc_info.challenge.hex()}, partial hash {block.reward_chain_block.get_hash()}"
588
588
)
589
589
# This proof is on an outdated challenge, so don't use it
590
590
continue
@@@ -593,13 -593,13 +593,13 @@@
593
593
self.last_active_time = time.time()
594
594
log.debug(f"Generated infusion point for challenge: {challenge} iterations: {iteration}.")
595
595

596
-- overflow = is_overflow_block(self.constants, block.reward_chain_block.signage_point_index)
596
++ overflow = is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index))
597
597

598
598
if not self.last_state.can_infuse_block(overflow):
599
599
log.warning("Too many blocks, or overflow in new epoch, cannot infuse, discarding")
600
600
return
601
601

602
-- cc_info = dataclasses.replace(cc_info, number_of_iterations=ip_iters)
602
++ cc_info = cc_info.replace(number_of_iterations=ip_iters)
603
603
response = timelord_protocol.NewInfusionPointVDF(
604
604
challenge,
605
605
cc_info,
@@@ -628,7 -628,7 +628,7 @@@
628
628
+ calculate_sp_iters(
629
629
self.constants,
630
630
block.sub_slot_iters,
631
-- block.reward_chain_block.signage_point_index,
631
++ uint8(block.reward_chain_block.signage_point_index),
632
632
)
633
633
- (block.sub_slot_iters if overflow else 0)
634
634
)
@@@ -755,14 -755,14 +755,14 @@@
755
755
rc_challenge = self.last_state.get_challenge(Chain.REWARD_CHAIN)
756
756
if rc_vdf.challenge != rc_challenge:
757
757
assert rc_challenge is not None
758
-- log.warning(f"Do not have correct challenge {rc_challenge.hex()} has {rc_vdf.challenge}")
758
++ log.warning(f"Do not have correct challenge {rc_challenge.hex()} has {rc_vdf.challenge.hex()}")
759
759
# This proof is on an outdated challenge, so don't use it
760
760
return
761
761
log.debug("Collected end of subslot vdfs.")
762
762
self.iters_finished.add(iter_to_look_for)
763
763
self.last_active_time = time.time()
764
764
iters_from_sub_slot_start = uint64(cc_vdf.number_of_iterations + self.last_state.get_last_ip())
765
-- cc_vdf = dataclasses.replace(cc_vdf, number_of_iterations=iters_from_sub_slot_start)
765
++ cc_vdf = cc_vdf.replace(number_of_iterations=iters_from_sub_slot_start)
766
766
if icc_ip_vdf is not None:
767
767
if self.last_state.peak is not None:
768
768
total_iters = (
@@@ -778,7 -778,7 +778,7 @@@
778
778
log.error(f"{self.last_state.subslot_end}")
779
779
assert False
780
780
assert iters_from_cb <= self.last_state.sub_slot_iters
781
-- icc_ip_vdf = dataclasses.replace(icc_ip_vdf, number_of_iterations=iters_from_cb)
781
++ icc_ip_vdf = icc_ip_vdf.replace(number_of_iterations=iters_from_cb)
782
782

783
783
icc_sub_slot: Optional[InfusedChallengeChainSubSlot] = (
784
784
None if icc_ip_vdf is None else InfusedChallengeChainSubSlot(icc_ip_vdf)
@@@ -1118,7 -1118,7 +1118,7 @@@
1118
1118
ip,
1119
1119
reader,
1120
1120
writer,
1121
-- info[1].new_proof_of_time.number_of_iterations,
1121
++ uint64(info[1].new_proof_of_time.number_of_iterations),
1122
1122
info[1].header_hash,
1123
1123
info[1].height,
1124
1124
info[1].field_vdf,
@@@ -1170,7 -1170,7 +1170,7 @@@
1170
1170
bluebox_process_data = BlueboxProcessData(
1171
1171
picked_info.new_proof_of_time.challenge,
1172
1172
uint16(self.constants.DISCRIMINANT_SIZE_BITS),
1173
-- picked_info.new_proof_of_time.number_of_iterations,
1173
++ uint64(picked_info.new_proof_of_time.number_of_iterations),
1174
1174
)
1175
1175
proof = await asyncio.get_running_loop().run_in_executor(
1176
1176
pool,
chia/timelord/timelord_state.py CHANGED
@@@ -59,13 -59,13 +59,13 @@@ class LastState
59
59
state.reward_chain_block,
60
60
state.sub_slot_iters,
61
61
state.difficulty,
62
-- state.reward_chain_block.height,
62
++ uint32(state.reward_chain_block.height),
63
63
)
64
64
self.deficit = state.deficit
65
65
self.sub_epoch_summary = state.sub_epoch_summary
66
-- self.last_weight = state.reward_chain_block.weight
67
-- self.last_height = state.reward_chain_block.height
68
-- self.total_iters = state.reward_chain_block.total_iters
66
++ self.last_weight = uint128(state.reward_chain_block.weight)
67
++ self.last_height = uint32(state.reward_chain_block.height)
68
++ self.total_iters = uint128(state.reward_chain_block.total_iters)
69
69
self.last_peak_challenge = state.reward_chain_block.get_hash()
70
70
self.difficulty = state.difficulty
71
71
self.sub_slot_iters = state.sub_slot_iters
@@@ -87,11 -87,11 +87,11 @@@
87
87
self.peak = None
88
88
self.subslot_end = state
89
89
self.last_ip = uint64(0)
90
-- self.deficit = state.reward_chain.deficit
90
++ self.deficit = uint8(state.reward_chain.deficit)
91
91
if state.challenge_chain.new_difficulty is not None:
92
92
assert state.challenge_chain.new_sub_slot_iters is not None
93
-- self.difficulty = state.challenge_chain.new_difficulty
94
-- self.sub_slot_iters = state.challenge_chain.new_sub_slot_iters
93
++ self.difficulty = uint64(state.challenge_chain.new_difficulty)
94
++ self.sub_slot_iters = uint64(state.challenge_chain.new_sub_slot_iters)
95
95
self.new_epoch = True
96
96
else:
97
97
self.new_epoch = False
chia/types/blockchain_format/classgroup.py CHANGED
@@@ -1,34 -1,34 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
3
++ from chia_rs import ClassgroupElement
4
4

5
-- from chia.types.blockchain_format.sized_bytes import bytes100
6
-- from chia.util.streamable import Streamable, streamable
7
--
8
--
9
-- @streamable
10
-- @dataclass(frozen=True)
11
-- class ClassgroupElement(Streamable):
12
-- """
13
-- Represents a classgroup element (a,b,c) where a, b, and c are 512 bit signed integers. However this is using
14
-- a compressed representation. VDF outputs are a single classgroup element. VDF proofs can also be one classgroup
15
-- element (or multiple).
16
-- """
17
--
18
-- data: bytes100
19
--
20
-- @staticmethod
21
-- def create(data: bytes) -> ClassgroupElement:
22
-- if len(data) < 100:
23
-- data += b"\x00" * (100 - len(data))
24
-- return ClassgroupElement(bytes100(data))
25
--
26
-- @staticmethod
27
-- def get_default_element() -> ClassgroupElement:
28
-- # Bit 3 in the first byte of serialized compressed form indicates if
29
-- # it's the default generator element.
30
-- return ClassgroupElement.create(b"\x08")
31
--
32
-- @staticmethod
33
-- def get_size() -> int:
34
-- return 100
5
++ __all__ = ["ClassgroupElement"]
chia/types/blockchain_format/foliage.py CHANGED
@@@ -1,61 -1,61 +1,8 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
4
-- from typing import List, Optional
3
++ import chia_rs
5
4

6
-- from chia_rs import G2Element
7
--
8
-- from chia.types.blockchain_format.coin import Coin
9
-- from chia.types.blockchain_format.pool_target import PoolTarget
10
-- from chia.types.blockchain_format.sized_bytes import bytes32
11
-- from chia.util.ints import uint64
12
-- from chia.util.streamable import Streamable, streamable
13
--
14
--
15
-- @streamable
16
-- @dataclass(frozen=True)
17
-- class TransactionsInfo(Streamable):
18
-- # Information that goes along with each transaction block
19
-- generator_root: bytes32 # sha256 of the block generator in this block
20
-- generator_refs_root: bytes32 # sha256 of the concatenation of the generator ref list entries
21
-- aggregated_signature: G2Element
22
-- fees: uint64 # This only includes user fees, not block rewards
23
-- cost: uint64 # This is the total cost of this block, including CLVM cost, cost of program size and conditions
24
-- reward_claims_incorporated: List[Coin] # These can be in any order
25
--
26
--
27
-- @streamable
28
-- @dataclass(frozen=True)
29
-- class FoliageTransactionBlock(Streamable):
30
-- # Information that goes along with each transaction block that is relevant for light clients
31
-- prev_transaction_block_hash: bytes32
32
-- timestamp: uint64
33
-- filter_hash: bytes32
34
-- additions_root: bytes32
35
-- removals_root: bytes32
36
-- transactions_info_hash: bytes32
37
--
38
--
39
-- @streamable
40
-- @dataclass(frozen=True)
41
-- class FoliageBlockData(Streamable):
42
-- # Part of the block that is signed by the plot key
43
-- unfinished_reward_block_hash: bytes32
44
-- pool_target: PoolTarget
45
-- pool_signature: Optional[G2Element] # Iff ProofOfSpace has a pool pk
46
-- farmer_reward_puzzle_hash: bytes32
47
-- extension_data: bytes32 # Used for future updates. Can be any 32 byte value initially
48
--
49
--
50
-- @streamable
51
-- @dataclass(frozen=True)
52
-- class Foliage(Streamable):
53
-- # The entire foliage block, containing signature and the unsigned back pointer
54
-- # The hash of this is the "header hash". Note that for unfinished blocks, the prev_block_hash
55
-- # Is the prev from the signage point, and can be replaced with a more recent block
56
-- prev_block_hash: bytes32
57
-- reward_block_hash: bytes32
58
-- foliage_block_data: FoliageBlockData
59
-- foliage_block_data_signature: G2Element
60
-- foliage_transaction_block_hash: Optional[bytes32]
61
-- foliage_transaction_block_signature: Optional[G2Element]
5
++ TransactionsInfo = chia_rs.TransactionsInfo
6
++ FoliageTransactionBlock = chia_rs.FoliageTransactionBlock
7
++ FoliageBlockData = chia_rs.FoliageBlockData
8
++ Foliage = chia_rs.Foliage
chia/types/blockchain_format/pool_target.py CHANGED
@@@ -1,14 -1,14 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
3
++ import chia_rs
4
4

5
-- from chia.types.blockchain_format.sized_bytes import bytes32
6
-- from chia.util.ints import uint32
7
-- from chia.util.streamable import Streamable, streamable
8
--
9
--
10
-- @streamable
11
-- @dataclass(frozen=True)
12
-- class PoolTarget(Streamable):
13
-- puzzle_hash: bytes32
14
-- max_height: uint32 # A max height of 0 means it is valid forever
5
++ PoolTarget = chia_rs.PoolTarget
chia/types/blockchain_format/proof_of_space.py CHANGED
@@@ -1,9 -1,9 +1,9 @@@
1
1
from __future__ import annotations
2
2

3
3
import logging
4
-- from dataclasses import dataclass
5
4
from typing import Optional, cast
6
5

6
++ import chia_rs
7
7
from bitstring import BitArray
8
8
from chia_rs import AugSchemeMPL, G1Element, PrivateKey
9
9
from chiapos import Verifier
@@@ -11,21 -11,21 +11,11 @@@
11
11
from chia.consensus.constants import ConsensusConstants
12
12
from chia.types.blockchain_format.sized_bytes import bytes32
13
13
from chia.util.hash import std_hash
14
-- from chia.util.ints import uint8, uint32
15
-- from chia.util.streamable import Streamable, streamable
16
--
17
-- log = logging.getLogger(__name__)
14
++ from chia.util.ints import uint32
18
15

16
++ ProofOfSpace = chia_rs.ProofOfSpace
19
17

20
-- @streamable
21
-- @dataclass(frozen=True)
22
-- class ProofOfSpace(Streamable):
23
-- challenge: bytes32
24
-- pool_public_key: Optional[G1Element] # Only one of these two should be present
25
-- pool_contract_puzzle_hash: Optional[bytes32]
26
-- plot_public_key: G1Element
27
-- size: uint8
28
-- proof: bytes
18
++ log = logging.getLogger(__name__)
29
19

30
20

31
21
def get_plot_id(pos: ProofOfSpace) -> bytes32:
chia/types/blockchain_format/reward_chain_block.py CHANGED
@@@ -1,56 -1,56 +1,6 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
4
-- from typing import Optional
3
++ import chia_rs
5
4

6
-- from chia_rs import G2Element
7
--
8
-- from chia.types.blockchain_format.proof_of_space import ProofOfSpace
9
-- from chia.types.blockchain_format.sized_bytes import bytes32
10
-- from chia.types.blockchain_format.vdf import VDFInfo
11
-- from chia.util.ints import uint8, uint32, uint128
12
-- from chia.util.streamable import Streamable, streamable
13
--
14
--
15
-- @streamable
16
-- @dataclass(frozen=True)
17
-- class RewardChainBlockUnfinished(Streamable):
18
-- total_iters: uint128
19
-- signage_point_index: uint8
20
-- pos_ss_cc_challenge_hash: bytes32
21
-- proof_of_space: ProofOfSpace
22
-- challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
23
-- challenge_chain_sp_signature: G2Element
24
-- reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
25
-- reward_chain_sp_signature: G2Element
26
--
27
--
28
-- @streamable
29
-- @dataclass(frozen=True)
30
-- class RewardChainBlock(Streamable):
31
-- weight: uint128
32
-- height: uint32
33
-- total_iters: uint128
34
-- signage_point_index: uint8
35
-- pos_ss_cc_challenge_hash: bytes32
36
-- proof_of_space: ProofOfSpace
37
-- challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
38
-- challenge_chain_sp_signature: G2Element
39
-- challenge_chain_ip_vdf: VDFInfo
40
-- reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
41
-- reward_chain_sp_signature: G2Element
42
-- reward_chain_ip_vdf: VDFInfo
43
-- infused_challenge_chain_ip_vdf: Optional[VDFInfo] # Iff deficit < 16
44
-- is_transaction_block: bool
45
--
46
-- def get_unfinished(self) -> RewardChainBlockUnfinished:
47
-- return RewardChainBlockUnfinished(
48
-- self.total_iters,
49
-- self.signage_point_index,
50
-- self.pos_ss_cc_challenge_hash,
51
-- self.proof_of_space,
52
-- self.challenge_chain_sp_vdf,
53
-- self.challenge_chain_sp_signature,
54
-- self.reward_chain_sp_vdf,
55
-- self.reward_chain_sp_signature,
56
-- )
5
++ RewardChainBlock = chia_rs.RewardChainBlock
6
++ RewardChainBlockUnfinished = chia_rs.RewardChainBlockUnfinished
chia/types/blockchain_format/serialized_program.py CHANGED
@@@ -1,115 -1,115 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- import io
4
-- from typing import Tuple
3
++ import chia_rs
5
4

6
-- from chia_rs import MEMPOOL_MODE, run_chia_program, serialized_length, tree_hash
7
-- from clvm import SExp
8
-- from clvm.SExp import CastableType
9
--
10
-- from chia.types.blockchain_format.program import Program
11
-- from chia.types.blockchain_format.sized_bytes import bytes32
12
-- from chia.util.byte_types import hexstr_to_bytes
13
--
14
--
15
-- def _serialize(node: object) -> bytes:
16
-- if isinstance(node, list):
17
-- serialized_list = bytearray()
18
-- for a in node:
19
-- serialized_list += b"\xff"
20
-- serialized_list += _serialize(a)
21
-- serialized_list += b"\x80"
22
-- return bytes(serialized_list)
23
-- if type(node) is SerializedProgram:
24
-- return bytes(node)
25
-- if type(node) is Program:
26
-- return bytes(node)
27
-- else:
28
-- ret: bytes = SExp.to(node).as_bin()
29
-- return ret
30
--
31
--
32
-- class SerializedProgram:
33
-- """
34
-- An opaque representation of a clvm program. It has a more limited interface than a full SExp
35
-- """
36
--
37
-- _buf: bytes
38
--
39
-- def __init__(self, buf: bytes) -> None:
40
-- assert isinstance(buf, bytes)
41
-- self._buf = buf
42
--
43
-- @staticmethod
44
-- def parse(f: io.BytesIO) -> SerializedProgram:
45
-- length = serialized_length(f.getvalue()[f.tell() :])
46
-- return SerializedProgram.from_bytes(f.read(length))
47
--
48
-- def stream(self, f: io.BytesIO) -> None:
49
-- f.write(self._buf)
50
--
51
-- @staticmethod
52
-- def from_bytes(blob: bytes) -> SerializedProgram:
53
-- assert serialized_length(blob) == len(blob)
54
-- return SerializedProgram(bytes(blob))
55
--
56
-- @staticmethod
57
-- def fromhex(hexstr: str) -> SerializedProgram:
58
-- return SerializedProgram.from_bytes(hexstr_to_bytes(hexstr))
59
--
60
-- @staticmethod
61
-- def from_program(p: Program) -> SerializedProgram:
62
-- return SerializedProgram(bytes(p))
63
--
64
-- @staticmethod
65
-- def to(o: CastableType) -> SerializedProgram:
66
-- return SerializedProgram(Program.to(o).as_bin())
67
--
68
-- def to_program(self) -> Program:
69
-- return Program.from_bytes(self._buf)
70
--
71
-- def uncurry(self) -> Tuple[Program, Program]:
72
-- return self.to_program().uncurry()
73
--
74
-- def __bytes__(self) -> bytes:
75
-- return self._buf
76
--
77
-- def __str__(self) -> str:
78
-- return bytes(self).hex()
79
--
80
-- def __repr__(self) -> str:
81
-- return f"{self.__class__.__name__}({str(self)})"
82
--
83
-- def __eq__(self, other: object) -> bool:
84
-- if not isinstance(other, SerializedProgram):
85
-- return False
86
-- return self._buf == other._buf
87
--
88
-- def __ne__(self, other: object) -> bool:
89
-- if not isinstance(other, SerializedProgram):
90
-- return True
91
-- return self._buf != other._buf
92
--
93
-- def get_tree_hash(self) -> bytes32:
94
-- return bytes32(tree_hash(self._buf))
95
--
96
-- def run_mempool_with_cost(self, max_cost: int, arg: object) -> Tuple[int, Program]:
97
-- return self._run(max_cost, MEMPOOL_MODE, arg)
98
--
99
-- def run_with_cost(self, max_cost: int, arg: object) -> Tuple[int, Program]:
100
-- return self._run(max_cost, 0, arg)
101
--
102
-- def _run(self, max_cost: int, flags: int, arg: object) -> Tuple[int, Program]:
103
-- # when multiple arguments are passed, concatenate them into a serialized
104
-- # buffer. Some arguments may already be in serialized form (e.g.
105
-- # SerializedProgram) so we don't want to de-serialize those just to
106
-- # serialize them back again. This is handled by _serialize()
107
-- serialized_args = _serialize(arg)
108
--
109
-- cost, ret = run_chia_program(
110
-- self._buf,
111
-- bytes(serialized_args),
112
-- max_cost,
113
-- flags,
114
-- )
115
-- return cost, Program.to(ret)
5
++ SerializedProgram = chia_rs.Program
chia/types/blockchain_format/slots.py CHANGED
@@@ -1,54 -1,54 +1,9 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
4
-- from typing import Optional
3
++ import chia_rs
5
4

6
-- from chia_rs import G2Element
7
--
8
-- from chia.types.blockchain_format.proof_of_space import ProofOfSpace
9
-- from chia.types.blockchain_format.sized_bytes import bytes32
10
-- from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
11
-- from chia.util.ints import uint8, uint64
12
-- from chia.util.streamable import Streamable, streamable
13
--
14
--
15
-- @streamable
16
-- @dataclass(frozen=True)
17
-- class ChallengeBlockInfo(Streamable): # The hash of this is used as the challenge_hash for the ICC VDF
18
-- proof_of_space: ProofOfSpace
19
-- challenge_chain_sp_vdf: Optional[VDFInfo] # Only present if not the first sp
20
-- challenge_chain_sp_signature: G2Element
21
-- challenge_chain_ip_vdf: VDFInfo
22
--
23
--
24
-- @streamable
25
-- @dataclass(frozen=True)
26
-- class ChallengeChainSubSlot(Streamable):
27
-- challenge_chain_end_of_slot_vdf: VDFInfo
28
-- infused_challenge_chain_sub_slot_hash: Optional[bytes32] # Only at the end of a slot
29
-- subepoch_summary_hash: Optional[bytes32] # Only once per sub-epoch, and one sub-epoch delayed
30
-- new_sub_slot_iters: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot
31
-- new_difficulty: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot
32
--
33
--
34
-- @streamable
35
-- @dataclass(frozen=True)
36
-- class InfusedChallengeChainSubSlot(Streamable):
37
-- infused_challenge_chain_end_of_slot_vdf: VDFInfo
38
--
39
--
40
-- @streamable
41
-- @dataclass(frozen=True)
42
-- class RewardChainSubSlot(Streamable):
43
-- end_of_slot_vdf: VDFInfo
44
-- challenge_chain_sub_slot_hash: bytes32
45
-- infused_challenge_chain_sub_slot_hash: Optional[bytes32]
46
-- deficit: uint8 # 16 or less. usually zero
47
--
48
--
49
-- @streamable
50
-- @dataclass(frozen=True)
51
-- class SubSlotProofs(Streamable):
52
-- challenge_chain_slot_proof: VDFProof
53
-- infused_challenge_chain_slot_proof: Optional[VDFProof]
54
-- reward_chain_slot_proof: VDFProof
5
++ ChallengeBlockInfo = chia_rs.ChallengeBlockInfo
6
++ ChallengeChainSubSlot = chia_rs.ChallengeChainSubSlot
7
++ InfusedChallengeChainSubSlot = chia_rs.InfusedChallengeChainSubSlot
8
++ RewardChainSubSlot = chia_rs.RewardChainSubSlot
9
++ SubSlotProofs = chia_rs.SubSlotProofs
chia/types/blockchain_format/sub_epoch_summary.py CHANGED
@@@ -1,18 -1,18 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
4
-- from typing import Optional
3
++ import chia_rs
5
4

6
-- from chia.types.blockchain_format.sized_bytes import bytes32
7
-- from chia.util.ints import uint8, uint64
8
-- from chia.util.streamable import Streamable, streamable
9
--
10
--
11
-- @streamable
12
-- @dataclass(frozen=True)
13
-- class SubEpochSummary(Streamable):
14
-- prev_subepoch_summary_hash: bytes32
15
-- reward_chain_hash: bytes32 # hash of reward chain at end of last segment
16
-- num_blocks_overflow: uint8 # How many more blocks than 384*(N-1)
17
-- new_difficulty: Optional[uint64] # Only once per epoch (diff adjustment)
18
-- new_sub_slot_iters: Optional[uint64] # Only once per epoch (diff adjustment)
5
++ SubEpochSummary = chia_rs.SubEpochSummary
chia/types/blockchain_format/vdf.py CHANGED
@@@ -2,21 -2,21 +2,22 @@@ from __future__ import annotation
2
2

3
3
import logging
4
4
import traceback
5
-- from dataclasses import dataclass
6
5
from enum import IntEnum
7
6
from functools import lru_cache
8
7
from typing import Optional
9
8

9
++ from chia_rs import VDFInfo, VDFProof
10
10
from chiavdf import create_discriminant, verify_n_wesolowski
11
11

12
12
from chia.consensus.constants import ConsensusConstants
13
13
from chia.types.blockchain_format.classgroup import ClassgroupElement
14
14
from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
15
15
from chia.util.ints import uint8, uint64
16
-- from chia.util.streamable import Streamable, streamable
17
16

18
17
log = logging.getLogger(__name__)
19
18

19
++ __all__ = ["VDFInfo", "VDFProof"]
20
++
20
21

21
22
@lru_cache(maxsize=200)
22
23
def get_discriminant(challenge: bytes32, size_bites: int) -> int:
@@@ -46,22 -46,22 +47,6 @@@ def verify_vdf
46
47
)
47
48

48
49

49
-- @streamable
50
-- @dataclass(frozen=True)
51
-- class VDFInfo(Streamable):
52
-- challenge: bytes32 # Used to generate the discriminant (VDF group)
53
-- number_of_iterations: uint64
54
-- output: ClassgroupElement
55
--
56
--
57
-- @streamable
58
-- @dataclass(frozen=True)
59
-- class VDFProof(Streamable):
60
-- witness_type: uint8
61
-- witness: bytes
62
-- normalized_to_identity: bool
63
--
64
--
65
50
def validate_vdf(
66
51
proof: VDFProof,
67
52
constants: ConsensusConstants,
chia/types/end_of_slot_bundle.py CHANGED
@@@ -1,21 -1,21 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- from dataclasses import dataclass
4
-- from typing import Optional
3
++ import chia_rs
5
4

6
-- from chia.types.blockchain_format.slots import (
7
-- ChallengeChainSubSlot,
8
-- InfusedChallengeChainSubSlot,
9
-- RewardChainSubSlot,
10
-- SubSlotProofs,
11
-- )
12
-- from chia.util.streamable import Streamable, streamable
13
--
14
--
15
-- @streamable
16
-- @dataclass(frozen=True)
17
-- class EndOfSubSlotBundle(Streamable):
18
-- challenge_chain: ChallengeChainSubSlot
19
-- infused_challenge_chain: Optional[InfusedChallengeChainSubSlot]
20
-- reward_chain: RewardChainSubSlot
21
-- proofs: SubSlotProofs
5
++ EndOfSubSlotBundle = chia_rs.EndOfSubSlotBundle
chia/types/full_block.py CHANGED
@@@ -39,15 -39,15 +39,15 @@@ class FullBlock(Streamable)
39
39

40
40
@property
41
41
def height(self) -> uint32:
42
-- return self.reward_chain_block.height
42
++ return uint32(self.reward_chain_block.height)
43
43

44
44
@property
45
45
def weight(self) -> uint128:
46
-- return self.reward_chain_block.weight
46
++ return uint128(self.reward_chain_block.weight)
47
47

48
48
@property
49
49
def total_iters(self) -> uint128:
50
-- return self.reward_chain_block.total_iters
50
++ return uint128(self.reward_chain_block.total_iters)
51
51

52
52
@property
53
53
def header_hash(self) -> bytes32:
chia/types/header_block.py CHANGED
@@@ -38,11 -38,11 +38,11 @@@ class HeaderBlock(Streamable)
38
38

39
39
@property
40
40
def height(self) -> uint32:
41
-- return self.reward_chain_block.height
41
++ return uint32(self.reward_chain_block.height)
42
42

43
43
@property
44
44
def weight(self) -> uint128:
45
-- return self.reward_chain_block.weight
45
++ return uint128(self.reward_chain_block.weight)
46
46

47
47
@property
48
48
def header_hash(self) -> bytes32:
@@@ -50,7 -50,7 +50,7 @@@
50
50

51
51
@property
52
52
def total_iters(self) -> uint128:
53
-- return self.reward_chain_block.total_iters
53
++ return uint128(self.reward_chain_block.total_iters)
54
54

55
55
@property
56
56
def log_string(self) -> str:
chia/types/transaction_queue_entry.py CHANGED
@@@ -1,6 -1,6 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- import asyncio
4
3
from dataclasses import dataclass, field
5
4
from typing import Optional, Tuple
6
5

@@@ -9,6 -9,6 +8,7 @@@ from chia.types.blockchain_format.sized
9
8
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
10
9
from chia.types.spend_bundle import SpendBundle
11
10
from chia.util.errors import Err
11
++ from chia.util.misc import ValuedEvent
12
12

13
13

14
14
@dataclass(frozen=True)
@@@ -22,7 -22,7 +22,7 @@@ class TransactionQueueEntry
22
22
spend_name: bytes32
23
23
peer: Optional[WSChiaConnection] = field(compare=False)
24
24
test: bool = field(compare=False)
25
-- done: asyncio.Future[Tuple[MempoolInclusionStatus, Optional[Err]]] = field(
26
-- default_factory=asyncio.Future,
25
++ done: ValuedEvent[Tuple[MempoolInclusionStatus, Optional[Err]]] = field(
26
++ default_factory=ValuedEvent,
27
27
compare=False,
28
28
)
chia/types/unfinished_block.py CHANGED
@@@ -42,4 -42,4 +42,4 @@@ class UnfinishedBlock(Streamable)
42
42

43
43
@property
44
44
def total_iters(self) -> uint128:
45
-- return self.reward_chain_block.total_iters
45
++ return uint128(self.reward_chain_block.total_iters)
chia/types/unfinished_header_block.py CHANGED
@@@ -34,4 -34,4 +34,4 @@@ class UnfinishedHeaderBlock(Streamable)
34
34

35
35
@property
36
36
def total_iters(self) -> uint128:
37
-- return self.reward_chain_block.total_iters
37
++ return uint128(self.reward_chain_block.total_iters)
chia/types/weight_proof.py CHANGED
@@@ -1,26 -1,26 +1,16 @@@
1
1
from __future__ import annotations
2
2

3
3
from dataclasses import dataclass
4
-- from typing import List, Optional
4
++ from typing import List
5
++
6
++ import chia_rs
5
7

6
-- from chia.types.blockchain_format.proof_of_space import ProofOfSpace
7
8
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
8
-- from chia.types.blockchain_format.sized_bytes import bytes32
9
-- from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
10
9
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
11
10
from chia.types.header_block import HeaderBlock
12
-- from chia.util.ints import uint8, uint32, uint64, uint128
13
11
from chia.util.streamable import Streamable, streamable
14
12

15
--
16
-- @streamable
17
-- @dataclass(frozen=True)
18
-- class SubEpochData(Streamable):
19
-- reward_chain_hash: bytes32
20
-- num_blocks_overflow: uint8
21
-- new_sub_slot_iters: Optional[uint64]
22
-- new_difficulty: Optional[uint64]
23
--
13
++ SubEpochData = chia_rs.SubEpochData
24
14

25
15
# number of challenge blocks
26
16
# Average iters for challenge blocks
@@@ -33,53 -33,53 +23,9 @@@
33
23
# total number of challenge blocks == total number of reward chain blocks
34
24

35
25

36
-- @streamable
37
-- @dataclass(frozen=True)
38
-- class SubSlotData(Streamable):
39
-- # if infused
40
-- proof_of_space: Optional[ProofOfSpace]
41
-- # VDF to signage point
42
-- cc_signage_point: Optional[VDFProof]
43
-- # VDF from signage to infusion point
44
-- cc_infusion_point: Optional[VDFProof]
45
-- icc_infusion_point: Optional[VDFProof]
46
-- cc_sp_vdf_info: Optional[VDFInfo]
47
-- signage_point_index: Optional[uint8]
48
-- # VDF from beginning to end of slot if not infused
49
-- # from ip to end if infused
50
-- cc_slot_end: Optional[VDFProof]
51
-- icc_slot_end: Optional[VDFProof]
52
-- # info from finished slots
53
-- cc_slot_end_info: Optional[VDFInfo]
54
-- icc_slot_end_info: Optional[VDFInfo]
55
-- cc_ip_vdf_info: Optional[VDFInfo]
56
-- icc_ip_vdf_info: Optional[VDFInfo]
57
-- total_iters: Optional[uint128]
58
--
59
-- def is_challenge(self) -> bool:
60
-- if self.proof_of_space is not None:
61
-- return True
62
-- return False
63
--
64
-- def is_end_of_slot(self) -> bool:
65
-- if self.cc_slot_end_info is not None:
66
-- return True
67
-- return False
68
--
69
--
70
-- @streamable
71
-- @dataclass(frozen=True)
72
-- class SubEpochChallengeSegment(Streamable):
73
-- sub_epoch_n: uint32
74
-- sub_slots: List[SubSlotData]
75
-- rc_slot_end_info: Optional[VDFInfo] # in first segment of each sub_epoch
76
--
77
--
78
-- @streamable
79
-- @dataclass(frozen=True)
80
-- # this is used only for serialization to database
81
-- class SubEpochSegments(Streamable):
82
-- challenge_segments: List[SubEpochChallengeSegment]
26
++ SubEpochChallengeSegment = chia_rs.SubEpochChallengeSegment
27
++ SubEpochSegments = chia_rs.SubEpochSegments
28
++ SubSlotData = chia_rs.SubSlotData
83
29

84
30

85
31
@streamable
chia/util/full_block_utils.py CHANGED
@@@ -1,6 -1,6 +1,5 @@@
1
1
from __future__ import annotations
2
2

3
-- import io
4
3
from dataclasses import dataclass
5
4
from typing import Callable, List, Optional, Tuple
6
5

@@@ -298,8 -298,8 +297,7 @@@ def header_block_from_block
298
297
transactions_info_optional = bytes([0])
299
298
else:
300
299
transactions_info_optional = bytes([1])
301
-- buf3 = buf2[1:]
302
-- transactions_info = TransactionsInfo.parse(io.BytesIO(buf3))
300
++ transactions_info, advance = TransactionsInfo.parse_rust(buf2[1:])
303
301
byte_array_tx: List[bytearray] = []
304
302
if is_transaction_block and transactions_info:
305
303
addition_coins = tx_addition_coins + list(transactions_info.reward_claims_incorporated)
chia/util/initial-config.yaml CHANGED
@@@ -440,6 -440,6 +440,11 @@@ full_node
440
440
# analyze with chia/utils/profiler.py
441
441
enable_profiler: False
442
442

443
++ # when enabled, each time a block is validated, the python profiler is
444
++ # engaged. If the validation takes more than 2 seconds, the profile is saved
445
++ # to disk, in the chia root/block-validation-profile
446
++ profile_block_validation: False
447
++
443
448
enable_memory_profiler: False
444
449

445
450
# this is a debug and profiling facility that logs all SQLite commands to a
chia/util/keyring_wrapper.py CHANGED
@@@ -2,7 -2,7 +2,7 @@@ from __future__ import annotation
2
2

3
3
from pathlib import Path
4
4
from sys import platform
5
-- from typing import Optional, Tuple, Union, overload
5
++ from typing import ClassVar, Optional, Tuple, Union, overload
6
6

7
7
from keyring.backends.macOS import Keyring as MacKeyring
8
8
from keyring.backends.Windows import WinVaultKeyring as WinKeyring
@@@ -63,8 -63,8 +63,8 @@@ class KeyringWrapper
63
63
"""
64
64

65
65
# Static members
66
-- __shared_instance = None
67
-- __keys_root_path: Path = DEFAULT_KEYS_ROOT_PATH
66
++ __shared_instance: ClassVar[Optional[KeyringWrapper]] = None
67
++ __keys_root_path: ClassVar[Path] = DEFAULT_KEYS_ROOT_PATH
68
68

69
69
# Instance members
70
70
keys_root_path: Path
chia/util/misc.py CHANGED
@@@ -13,6 -13,6 +13,7 @@@ from typing import
13
13
Any,
14
14
AsyncContextManager,
15
15
AsyncIterator,
16
++ ClassVar,
16
17
Collection,
17
18
ContextManager,
18
19
Dict,
@@@ -374,3 -374,3 +375,27 @@@ async def split_async_manager(manager:
374
375
yield split
375
376
finally:
376
377
await split.exit(if_needed=True)
378
++
379
++
380
++ class ValuedEventSentinel:
381
++ pass
382
++
383
++
384
++ @dataclasses.dataclass
385
++ class ValuedEvent(Generic[T]):
386
++ _value_sentinel: ClassVar[ValuedEventSentinel] = ValuedEventSentinel()
387
++
388
++ _event: asyncio.Event = dataclasses.field(default_factory=asyncio.Event)
389
++ _value: Union[ValuedEventSentinel, T] = _value_sentinel
390
++
391
++ def set(self, value: T) -> None:
392
++ if not isinstance(self._value, ValuedEventSentinel):
393
++ raise Exception("Value already set")
394
++ self._value = value
395
++ self._event.set()
396
++
397
++ async def wait(self) -> T:
398
++ await self._event.wait()
399
++ if isinstance(self._value, ValuedEventSentinel):
400
++ raise Exception("Value not set despite event being set")
401
++ return self._value
chia/util/profiler.py CHANGED
@@@ -5,7 -5,7 +5,9 @@@ import cProfil
5
5
import logging
6
6
import pathlib
7
7
import tracemalloc
8
++ from contextlib import asynccontextmanager
8
9
from datetime import datetime
10
++ from typing import AsyncIterator, Optional
9
11

10
12
from chia.util.path import path_from_root
11
13

@@@ -176,3 -176,3 +178,17 @@@ async def mem_profile_task(root_path: p
176
178
counter += 1
177
179
finally:
178
180
tracemalloc.stop()
181
++
182
++
183
++ @asynccontextmanager
184
++ async def enable_profiler(profile: bool) -> AsyncIterator[Optional[cProfile.Profile]]:
185
++ if not profile:
186
++ yield None
187
++ return
188
++
189
++ # this is not covered by any unit tests as it's essentially test code
190
++ # itself. It's exercised manually when investigating performance issues
191
++ with cProfile.Profile() as pr: # pragma: no cover
192
++ pr.enable()
193
++ yield pr
194
++ pr.disable()
chia/util/recursive_replace.py CHANGED
@@@ -7,6 -7,6 +7,16 @@@ from typing import An
7
7
def recursive_replace(root_obj: Any, replace_str: str, replace_with: Any) -> Any:
8
8
split_str = replace_str.split(".")
9
9
if len(split_str) == 1:
10
-- return replace(root_obj, **{split_str[0]: replace_with})
10
++ # This check is here to support native types (implemented in Rust
11
++ # in chia_rs) that aren't dataclasses. They instead implement a
12
++ # replace() method in their python bindings.
13
++ if hasattr(root_obj, "replace"):
14
++ return root_obj.replace(**{split_str[0]: replace_with})
15
++ else:
16
++ return replace(root_obj, **{split_str[0]: replace_with})
11
17
sub_obj = recursive_replace(getattr(root_obj, split_str[0]), ".".join(split_str[1:]), replace_with)
12
-- return replace(root_obj, **{split_str[0]: sub_obj})
18
++ # See comment above
19
++ if hasattr(root_obj, "replace"):
20
++ return root_obj.replace(**{split_str[0]: sub_obj})
21
++ else:
22
++ return replace(root_obj, **{split_str[0]: sub_obj})
chia/util/struct_stream.py DELETED
@@@ -1,6 -1,6 +1,6 @@@
1
1
from __future__ import annotations
2
2

3
-- from typing import BinaryIO, ClassVar, SupportsIndex, SupportsInt, Type, TypeVar, Union
3
++ from typing import BinaryIO, ClassVar, Optional, SupportsIndex, SupportsInt, Type, TypeVar, Union
4
4

5
5
from typing_extensions import Protocol
6
6

@@@ -70,6 -70,6 +70,13 @@@ class StructStream(int)
70
70
if not (self.MINIMUM <= self <= self.MAXIMUM):
71
71
raise ValueError(f"Value {self} does not fit into {type(self).__name__}")
72
72

73
++ @classmethod
74
++ def construct_optional(cls: Type[_T_StructStream], val: Optional[int]) -> Optional[_T_StructStream]:
75
++ if val is None:
76
++ return None
77
++ else:
78
++ return cls(val)
79
++
73
80
@classmethod
74
81
def parse(cls: Type[_T_StructStream], f: BinaryIO) -> _T_StructStream:
75
82
read_bytes = f.read(cls.SIZE)
chia/wallet/block_record.py RENAMED
@@@ -1,37 -1,37 +1,0 @@@
1
-- from __future__ import annotations
2
--
3
-- from dataclasses import dataclass
4
-- from typing import List
5
--
6
-- from chia.types.blockchain_format.coin import Coin
7
-- from chia.types.header_block import HeaderBlock
8
-- from chia.util.streamable import Streamable, streamable
9
--
10
--
11
-- @streamable
12
-- @dataclass(frozen=True)
13
-- class HeaderBlockRecord(Streamable):
14
-- """
15
-- These are values that are stored in the wallet database, corresponding to information
16
-- that the wallet cares about in each block
17
-- """
18
--
19
-- header: HeaderBlock
20
-- additions: List[Coin] # A block record without additions is not finished
21
-- removals: List[Coin] # A block record without removals is not finished
22
--
23
-- @property
24
-- def header_hash(self):
25
-- return self.header.header_hash
26
--
27
-- @property
28
-- def prev_header_hash(self):
29
-- return self.header.prev_header_hash
30
--
31
-- @property
32
-- def height(self):
33
-- return self.header.height
34
--
35
-- @property
36
-- def transactions_filter(self):
37
-- return self.header.transactions_filter
chia/wallet/conditions.py CHANGED
@@@ -2,7 -2,7 +2,7 @@@ from __future__ import annotation
2
2

3
3
from abc import ABC, abstractmethod
4
4
from dataclasses import dataclass, fields, replace
5
-- from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, final, get_type_hints
5
++ from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, cast, final, get_type_hints
6
6

7
7
from chia_rs import G1Element
8
8
from clvm.casts import int_from_bytes, int_to_bytes
@@@ -733,8 -733,8 +733,8 @@@ class UnknownCondition(Condition)
733
733
args: List[Program]
734
734

735
735
def to_program(self) -> Program:
736
-- prog: Program = self.opcode.cons(self.args)
737
-- return prog
736
++ # TODO: Remove cast when we have proper hinting for this
737
++ return cast(Program, self.opcode.cons(Program.to(self.args)))
738
738

739
739
@classmethod
740
740
def from_program(cls, program: Program) -> UnknownCondition:
chia/wallet/did_wallet/did_wallet.py CHANGED
@@@ -428,6 -428,6 +428,7 @@@ class DIDWallet
428
428
)
429
429

430
430
await self.add_parent(coin.name(), future_parent)
431
++ await self.wallet_state_manager.add_interested_coin_ids([coin.name()])
431
432

432
433
def create_backup(self) -> str:
433
434
"""
@@@ -902,6 -902,6 +903,9 @@@
902
903
) -> Tuple[TransactionRecord, SpendBundle, str]:
903
904
"""
904
905
Create an attestment
906
++ TODO:
907
++ 1. We should use/respect `tx_config` (reuse_puzhash and co)
908
++ 2. We should take a fee as it's a requirement for every transaction function to do so
905
909
:param recovering_coin_name: Coin ID of the DID
906
910
:param newpuz: New puzzle hash
907
911
:param pubkey: New wallet pubkey
@@@ -1116,27 -1116,27 +1120,20 @@@
1116
1120
async def get_new_p2_inner_puzzle(self) -> Program:
1117
1121
return await self.standard_wallet.get_new_puzzle()
1118
1122

1119
-- async def get_new_did_innerpuz(self, origin_id=None) -> Program:
1123
++ async def get_new_did_innerpuz(self, origin_id: Optional[bytes32] = None) -> Program:
1120
1124
if self.did_info.origin_coin is not None:
1121
-- innerpuz = did_wallet_puzzles.create_innerpuz(
1122
-- p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
1123
-- recovery_list=self.did_info.backup_ids,
1124
-- num_of_backup_ids_needed=uint64(self.did_info.num_of_backup_ids_needed),
1125
-- launcher_id=self.did_info.origin_coin.name(),
1126
-- metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
1127
-- )
1125
++ launcher_id = self.did_info.origin_coin.name()
1128
1126
elif origin_id is not None:
1129
-- innerpuz = did_wallet_puzzles.create_innerpuz(
1130
-- p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
1131
-- recovery_list=self.did_info.backup_ids,
1132
-- num_of_backup_ids_needed=uint64(self.did_info.num_of_backup_ids_needed),
1133
-- launcher_id=origin_id,
1134
-- metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
1135
-- )
1127
++ launcher_id = origin_id
1136
1128
else:
1137
1129
raise ValueError("must have origin coin")
1138
--
1139
-- return innerpuz
1130
++ return did_wallet_puzzles.create_innerpuz(
1131
++ p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
1132
++ recovery_list=self.did_info.backup_ids,
1133
++ num_of_backup_ids_needed=self.did_info.num_of_backup_ids_needed,
1134
++ launcher_id=launcher_id,
1135
++ metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
1136
++ )
1140
1137

1141
1138
async def get_new_did_inner_hash(self) -> bytes32:
1142
1139
innerpuz = await self.get_new_did_innerpuz()
@@@ -1366,9 -1366,9 +1363,6 @@@
1366
1363
unsigned_spend_bundle = SpendBundle(list_of_coinspends, G2Element())
1367
1364
return await self.sign(unsigned_spend_bundle)
1368
1365

1369
-- async def get_frozen_amount(self) -> uint64:
1370
-- return await self.wallet_state_manager.get_frozen_balance(self.wallet_info.id)
1371
--
1372
1366
async def get_spendable_balance(self, unspent_records=None) -> uint128:
1373
1367
spendable_am = await self.wallet_state_manager.get_confirmed_spendable_balance_for_wallet(
1374
1368
self.wallet_info.id, unspent_records
@@@ -1509,7 -1509,7 +1503,7 @@@
1509
1503
)
1510
1504
if len(spendable_coins) == 0:
1511
1505
raise RuntimeError("DID is not currently spendable")
1512
-- return list(spendable_coins)[0].coin
1506
++ return sorted(list(spendable_coins), key=lambda c: c.confirmed_block_height, reverse=True)[0].coin
1513
1507

1514
1508
async def match_hinted_coin(self, coin: Coin, hint: bytes32) -> bool:
1515
1509
if self.did_info.origin_coin is None:
chia/wallet/did_wallet/did_wallet_puzzles.py CHANGED
@@@ -1,6 -1,6 +1,6 @@@
1
1
from __future__ import annotations
2
2

3
-- from typing import Dict, Iterator, List, Optional, Tuple, Union
3
++ from typing import Dict, Iterator, List, Optional, Tuple, Union, cast
4
4

5
5
from chia_rs import G1Element
6
6

@@@ -48,7 -48,7 +48,7 @@@ def create_innerpuz
48
48
Note: Receiving a standard P2 puzzle hash wouldn't calculate a valid puzzle, but
49
49
that can be useful if calling `.get_tree_hash_precalc()` on it.
50
50
"""
51
-- backup_ids_hash = Program(Program.to(recovery_list)).get_tree_hash()
51
++ backup_ids_hash = Program.to(recovery_list).get_tree_hash()
52
52
if recovery_list_hash is not None:
53
53
backup_ids_hash = recovery_list_hash
54
54
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
@@@ -123,7 -123,7 +123,7 @@@ def create_recovery_message_puzzle(reco
123
123
:param pubkey: New wallet pubkey
124
124
:return: Message puzzle
125
125
"""
126
-- return Program.to(
126
++ puzzle = Program.to(
127
127
(
128
128
1,
129
129
[
@@@ -132,6 -132,6 +132,8 @@@
132
132
],
133
133
)
134
134
)
135
++ # TODO: Remove cast when we have proper hinting for this
136
++ return cast(Program, puzzle)
135
137

136
138

137
139
def create_spend_for_message(
@@@ -153,7 -153,7 +155,7 @@@
153
155

154
156
def match_did_puzzle(mod: Program, curried_args: Program) -> Optional[Iterator[Program]]:
155
157
"""
156
-- Given a puzzle test if it's a DID, if it is, return the curried arguments
158
++ Given a puzzle test if it's a DID, if it is, return the curried arguments
157
159
:param puzzle: Puzzle
158
160
:return: Curried parameters
159
161
"""
@@@ -161,7 -161,7 +163,8 @@@
161
163
if mod == SINGLETON_TOP_LAYER_MOD:
162
164
mod, curried_args = curried_args.rest().first().uncurry()
163
165
if mod == DID_INNERPUZ_MOD:
164
-- return curried_args.as_iter()
166
++ # TODO: Remove cast when we have clvm type hinting for this
167
++ return cast(Iterator[Program], curried_args.as_iter())
165
168
except Exception:
166
169
import traceback
167
170

@@@ -178,11 -178,11 +181,11 @@@ def check_is_did_puzzle(puzzle: Program
178
181
r = puzzle.uncurry()
179
182
if r is None:
180
183
return False
181
-- inner_f, args = r
184
++ inner_f, _ = r
182
185
return is_singleton(inner_f)
183
186

184
187

185
-- def metadata_to_program(metadata: Dict) -> Program:
188
++ def metadata_to_program(metadata: Dict[str, str]) -> Program:
186
189
"""
187
190
Convert the metadata dict to a Chialisp program
188
191
:param metadata: User defined metadata
@@@ -191,10 -191,10 +194,11 @@@
191
194
kv_list = []
192
195
for key, value in metadata.items():
193
196
kv_list.append((key, value))
194
-- return Program.to(kv_list)
197
++ # TODO: Remove cast when we have proper hinting for this
198
++ return cast(Program, Program.to(kv_list))
195
199

196
200

197
-- def did_program_to_metadata(program: Program) -> Dict:
201
++ def did_program_to_metadata(program: Program) -> Dict[str, str]:
198
202
"""
199
203
Convert a program to a metadata dict
200
204
:param program: Chialisp program contains the metadata
chia/wallet/key_val_store.py CHANGED
@@@ -29,14 -29,14 +29,14 @@@ class KeyValStore
29
29
"""
30
30

31
31
async with self.db_wrapper.reader_no_transaction() as conn:
32
-- cursor = await conn.execute("SELECT * from key_val_store WHERE key=?", (key,))
32
++ cursor = await conn.execute("SELECT value from key_val_store WHERE key=?", (key,))
33
33
row = await cursor.fetchone()
34
34
await cursor.close()
35
35

36
36
if row is None:
37
37
return None
38
38

39
-- return object_type.from_bytes(row[1])
39
++ return object_type.from_bytes(row[0])
40
40

41
41
async def set_object(self, key: str, obj: Any):
42
42
"""
chia/wallet/lineage_proof.py CHANGED
@@@ -1,7 -1,7 +1,8 @@@
1
1
from __future__ import annotations
2
2

3
3
from dataclasses import dataclass
4
-- from typing import Any, List, Optional
4
++ from enum import Enum
5
++ from typing import Any, Dict, List, Optional
5
6

6
7
from chia.types.blockchain_format.program import Program
7
8
from chia.types.blockchain_format.sized_bytes import bytes32
@@@ -9,6 -9,6 +10,12 @@@ from chia.util.ints import uint6
9
10
from chia.util.streamable import Streamable, streamable
10
11

11
12

13
++ class LineageProofField(Enum):
14
++ PARENT_NAME = 1
15
++ INNER_PUZZLE_HASH = 2
16
++ AMOUNT = 3
17
++
18
++
12
19
@streamable
13
20
@dataclass(frozen=True)
14
21
class LineageProof(Streamable):
@@@ -16,6 -16,6 +23,27 @@@
16
23
inner_puzzle_hash: Optional[bytes32] = None
17
24
amount: Optional[uint64] = None
18
25

26
++ @classmethod
27
++ def from_program(cls, program: Program, fields: List[LineageProofField]) -> LineageProof:
28
++ lineage_proof_info: Dict[str, Any] = {}
29
++ field_iter = iter(fields)
30
++ program_iter = program.as_iter()
31
++ for program_value in program_iter:
32
++ field = next(field_iter)
33
++ if field == LineageProofField.PARENT_NAME:
34
++ lineage_proof_info["parent_name"] = bytes32(program_value.as_atom())
35
++ elif field == LineageProofField.INNER_PUZZLE_HASH:
36
++ lineage_proof_info["inner_puzzle_hash"] = bytes32(program_value.as_atom())
37
++ elif field == LineageProofField.AMOUNT:
38
++ lineage_proof_info["amount"] = uint64(program_value.as_int())
39
++ try:
40
++ next(field_iter)
41
++ raise ValueError("Mismatch between program data and fields information")
42
++ except StopIteration:
43
++ pass
44
++
45
++ return LineageProof(**lineage_proof_info)
46
++
19
47
def to_program(self) -> Program:
20
48
final_list: List[Any] = []
21
49
if self.parent_name is not None:
chia/wallet/puzzles/tails.py CHANGED
@@@ -217,7 -217,7 +217,7 @@@ class GenesisByIdOrSingleton(Limitation
217
217
def match(uncurried_mod: Program, curried_args: Program) -> Tuple[bool, List[Program]]: # pragma: no cover
218
218
if uncurried_mod == GENESIS_BY_ID_OR_SINGLETON_MOD:
219
219
genesis_id = curried_args.first()
220
-- return True, [genesis_id.as_atom()]
220
++ return True, [genesis_id]
221
221
else:
222
222
return False, []
223
223

chia/wallet/trade_manager.py CHANGED
@@@ -540,7 -540,7 +540,7 @@@ class TradeManager
540
540
fee_left_to_pay: uint64 = fee
541
541
# The access of the sorted keys here makes sure we create the XCH transaction first to make sure we pay fee
542
542
# with the XCH side of the offer and don't create an extra fee transaction in other wallets.
543
-- for id in sorted(coins_to_offer.keys()):
543
++ for id in sorted(coins_to_offer.keys(), key=lambda id: id != 1):
544
544
selected_coins = coins_to_offer[id]
545
545
if isinstance(id, int):
546
546
wallet = self.wallet_state_manager.wallets[id]
chia/wallet/util/merkle_utils.py CHANGED
@@@ -47,6 -47,6 +47,8 @@@ def build_merkle_tree_from_binary_tree(
47
47

48
48
def list_to_binary_tree(objects: List[Any]) -> Any:
49
49
size = len(objects)
50
++ if size == 0:
51
++ raise ValueError("Cannot build a tree out of 0 objects")
50
52
if size == 1:
51
53
return objects[0]
52
54
midpoint = (size + 1) >> 1
chia/wallet/util/peer_request_cache.py CHANGED
@@@ -41,7 -41,7 +41,7 @@@ class PeerRequestCache
41
41
if header_block.is_transaction_block:
42
42
assert header_block.foliage_transaction_block is not None
43
43
if self._timestamps.get(header_block.height) is None:
44
-- self._timestamps.put(header_block.height, header_block.foliage_transaction_block.timestamp)
44
++ self._timestamps.put(header_block.height, uint64(header_block.foliage_transaction_block.timestamp))
45
45

46
46
def get_block_request(self, start: uint32, end: uint32) -> Optional[asyncio.Task[Any]]:
47
47
return self._block_requests.get((start, end))
chia/wallet/vc_wallet/cr_cat_drivers.py CHANGED
@@@ -16,7 -16,7 +16,7 @@@ from chia.util.ints import uint16, uint
16
16
from chia.util.streamable import Streamable, streamable
17
17
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
18
18
from chia.wallet.conditions import AssertCoinAnnouncement
19
-- from chia.wallet.lineage_proof import LineageProof
19
++ from chia.wallet.lineage_proof import LineageProof, LineageProofField
20
20
from chia.wallet.payment import Payment
21
21
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
22
22
from chia.wallet.puzzles.singleton_top_layer_v1_1 import SINGLETON_LAUNCHER_HASH, SINGLETON_MOD_HASH
@@@ -314,10 -314,10 +314,14 @@@ class CRCAT
314
314
uncurried_puzzle: UncurriedPuzzle = uncurry_puzzle(spend.puzzle_reveal.to_program())
315
315
first_uncurried_cr_layer: UncurriedPuzzle = uncurry_puzzle(uncurried_puzzle.args.at("rrf"))
316
316
second_uncurried_cr_layer: UncurriedPuzzle = uncurry_puzzle(first_uncurried_cr_layer.mod)
317
++ lineage_proof = LineageProof.from_program(
318
++ spend.solution.to_program().at("rf"),
319
++ [LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
320
++ )
317
321
return CRCAT(
318
322
spend.coin,
319
323
bytes32(uncurried_puzzle.args.at("rf").as_atom()),
320
-- spend.solution.to_program().at("rf"),
324
++ lineage_proof,
321
325
[bytes32(ap.as_atom()) for ap in second_uncurried_cr_layer.args.at("rf").as_iter()],
322
326
second_uncurried_cr_layer.args.at("rrf"),
323
327
first_uncurried_cr_layer.args.at("rf").get_tree_hash(),
@@@ -359,6 -359,6 +363,7 @@@
359
363
raise ValueError(
360
364
"Previous spend was not a CR-CAT, nor did it properly remark the CR params"
361
365
) # pragma: no cover
366
++ authorized_providers = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
362
367
lineage_inner_puzhash: bytes32 = potential_cr_layer.get_tree_hash()
363
368
else:
364
369
# Otherwise the info we need will be in the puzzle reveal
@@@ -369,14 -369,14 +374,14 @@@
369
374
if conditions is None:
370
375
conditions = inner_puzzle.run(inner_solution)
371
376
inner_puzzle_hash: bytes32 = inner_puzzle.get_tree_hash()
377
++ authorized_providers = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
372
378
lineage_inner_puzhash = construct_cr_layer(
373
-- authorized_providers_as_prog,
379
++ authorized_providers,
374
380
proofs_checker,
375
381
inner_puzzle_hash, # type: ignore
376
382
).get_tree_hash_precalc(inner_puzzle_hash)
377
383

378
384
# Convert all of the old stuff into python
379
-- authorized_providers: List[bytes32] = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
380
385
new_lineage_proof: LineageProof = LineageProof(
381
386
parent_spend.coin.parent_coin_info,
382
387
lineage_inner_puzhash,
chia/wallet/vc_wallet/vc_drivers.py CHANGED
@@@ -237,16 -237,16 +237,10 @@@ def create_eml_covenant_morpher
237
237

238
238

239
239
def construct_exigent_metadata_layer(
240
-- metadata: Optional[bytes32],
241
-- transfer_program: Program,
242
-- inner_puzzle: Program,
240
++ metadata: Optional[Program], transfer_program: Program, inner_puzzle: Program
243
241
) -> Program:
244
242
return EXTIGENT_METADATA_LAYER.curry(
245
-- EXTIGENT_METADATA_LAYER_HASH,
246
-- metadata,
247
-- transfer_program,
248
-- transfer_program.get_tree_hash(),
249
-- inner_puzzle,
243
++ EXTIGENT_METADATA_LAYER_HASH, metadata, transfer_program, transfer_program.get_tree_hash(), inner_puzzle
250
244
)
251
245

252
246

chia/wallet/wallet_blockchain.py CHANGED
@@@ -99,8 -99,8 +99,8 @@@ class WalletBlockchain(BlockchainInterf
99
99
and block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters is not None
100
100
):
101
101
assert block.finished_sub_slots[0].challenge_chain.new_difficulty is not None # They both change together
102
-- sub_slot_iters: uint64 = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters
103
-- difficulty: uint64 = block.finished_sub_slots[0].challenge_chain.new_difficulty
102
++ sub_slot_iters = uint64(block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters)
103
++ difficulty = uint64(block.finished_sub_slots[0].challenge_chain.new_difficulty)
104
104
else:
105
105
sub_slot_iters = self._sub_slot_iters
106
106
difficulty = self._difficulty
@@@ -164,7 -164,7 +164,7 @@@
164
164
if timestamp is not None:
165
165
self._latest_timestamp = timestamp
166
166
elif block.foliage_transaction_block is not None:
167
-- self._latest_timestamp = block.foliage_transaction_block.timestamp
167
++ self._latest_timestamp = uint64(block.foliage_transaction_block.timestamp)
168
168
log.info(f"Peak set to: {self._peak.height} timestamp: {self._latest_timestamp}")
169
169

170
170
async def get_peak_block(self) -> Optional[HeaderBlock]:
chia/wallet/wallet_node.py CHANGED
@@@ -1058,7 -1058,7 +1058,7 @@@ class WalletNode
1058
1058
self.log.debug(f"get_timestamp_for_height_from_peer use cached block for height {request_height}")
1059
1059

1060
1060
if block is not None and block.foliage_transaction_block is not None:
1061
-- return block.foliage_transaction_block.timestamp
1061
++ return uint64(block.foliage_transaction_block.timestamp)
1062
1062

1063
1063
request_height -= 1
1064
1064

install.sh CHANGED
@@@ -210,7 -210,7 +210,9 @@@ elif [ "$(uname)" = "Linux" ]; the
210
210
echo "Installing on Arch Linux."
211
211
case $(uname -m) in
212
212
x86_64|aarch64)
213
-- sudo pacman ${PACMAN_AUTOMATED} -S --needed git openssl
213
++ if ! pacman -Qs "^git$" > /dev/null || ! pacman -Qs "^openssl$" > /dev/null ; then
214
++ sudo pacman ${PACMAN_AUTOMATED} -S --needed git openssl
215
++ fi
214
216
;;
215
217
*)
216
218
echo "Incompatible CPU architecture. Must be x86_64 or aarch64."
mypy-exclusions.txt CHANGED
@@@ -11,7 -11,7 +11,6 @@@ chia.plotting.manage
11
11
chia.plotting.util
12
12
chia.rpc.rpc_client
13
13
chia.rpc.util
14
-- chia.rpc.wallet_rpc_client
15
14
chia.simulator.full_node_simulator
16
15
chia.simulator.keyring
17
16
chia.simulator.wallet_tools
@@@ -19,10 -19,10 +18,8 @@@ chia.ssl.create_ss
19
18
chia.timelord.timelord_api
20
19
chia.timelord.timelord_launcher
21
20
chia.types.blockchain_format.program
22
-- chia.wallet.block_record
23
21
chia.wallet.chialisp
24
22
chia.wallet.did_wallet.did_wallet
25
-- chia.wallet.did_wallet.did_wallet_puzzles
26
23
chia.wallet.key_val_store
27
24
chia.wallet.lineage_proof
28
25
chia.wallet.nft_wallet.nft_puzzles
@@@ -105,25 -105,25 +102,16 @@@ tests.util.test_full_block_util
105
102
tests.util.test_misc
106
103
tests.util.test_network
107
104
tests.util.time_out_assert
108
-- tests.wallet.cat_wallet.test_cat_wallet
109
-- tests.wallet.cat_wallet.test_offer_lifecycle
110
105
tests.wallet.cat_wallet.test_trades
111
106
tests.wallet.did_wallet.test_did
112
-- tests.wallet.nft_wallet.test_nft_wallet
113
107
tests.wallet.rpc.test_wallet_rpc
114
108
tests.wallet.simple_sync.test_simple_sync_protocol
115
109
tests.wallet.sync.test_wallet_sync
116
-- tests.wallet.test_bech32m
117
110
tests.wallet.test_chialisp
118
-- tests.wallet.test_puzzle_store
119
-- tests.wallet.test_singleton
120
111
tests.wallet.test_singleton_lifecycle
121
112
tests.wallet.test_singleton_lifecycle_fast
122
113
tests.wallet.test_taproot
123
-- tests.wallet.test_wallet_blockchain
124
114
tests.wallet.test_wallet_interested_store
125
115
tests.wallet.test_wallet_key_val_store
126
-- tests.wallet.test_wallet_user_store
127
116
tools.analyze-chain
128
117
tools.run_block
129
-- tools.test_full_sync
setup.py CHANGED
@@@ -7,14 -7,14 +7,14 @@@ from setuptools import find_packages, s
7
7

8
8
dependencies = [
9
9
"aiofiles==23.2.1", # Async IO for files
10
-- "anyio==4.1.0",
11
-- "boto3==1.34.0", # AWS S3 for DL s3 plugin
10
++ "anyio==4.2.0",
11
++ "boto3==1.34.11", # AWS S3 for DL s3 plugin
12
12
"chiavdf==1.1.1", # timelord and vdf verification
13
13
"chiabip158==1.3", # bip158-style wallet filters
14
14
"chiapos==2.0.3", # proof of space
15
15
"clvm==0.9.8",
16
16
"clvm_tools==0.4.7", # Currying, Program.to, other conveniences
17
-- "chia_rs==0.3.3",
17
++ "chia_rs==0.4.0",
18
18
"clvm-tools-rs==0.1.40", # Rust implementation of clvm_tools' compiler
19
19
"aiohttp==3.9.1", # HTTP server for full node rpc
20
20
"aiosqlite==0.19.0", # asyncio wrapper for sqlite, to store blocks
@@@ -32,7 -32,7 +32,7 @@@
32
32
"dnspython==2.4.2", # Query DNS seeds
33
33
"watchdog==2.2.0", # Filesystem event watching - watches keyring.yaml
34
34
"dnslib==0.9.23", # dns lib
35
-- "typing-extensions==4.8.0", # typing backports like Protocol and TypedDict
35
++ "typing-extensions==4.9.0", # typing backports like Protocol and TypedDict
36
36
"zstd==1.5.5.1",
37
37
"packaging==23.2",
38
38
"psutil==5.9.4",
@@@ -49,25 -49,25 +49,25 @@@ dev_dependencies =
49
49
"diff-cover==8.0.1",
50
50
"pre-commit==3.5.0; python_version < '3.9'",
51
51
"pre-commit==3.6.0; python_version >= '3.9'",
52
-- "py3createtorrent==1.1.0",
53
-- "pylint==3.0.2",
54
-- "pytest==7.4.3",
52
++ "py3createtorrent==1.2.0",
53
++ "pylint==3.0.3",
54
++ "pytest==7.4.4",
55
55
"pytest-cov==4.1.0",
56
56
"pytest-mock==3.12.0",
57
57
"pytest-xdist==3.5.0",
58
58
"pyupgrade==3.15.0",
59
59
"twine==4.0.2",
60
-- "isort==5.12.0",
61
-- "flake8==6.1.0",
62
-- "mypy==1.7.1",
63
-- "black==23.11.0",
60
++ "isort==5.13.2",
61
++ "flake8==7.0.0",
62
++ "mypy==1.8.0",
63
++ "black==23.12.1",
64
64
"lxml==4.9.3",
65
65
"aiohttp_cors==0.7.0", # For blackd
66
-- "pyinstaller==5.13.0",
67
-- "types-aiofiles==23.2.0.0",
66
++ "pyinstaller==6.3.0",
67
++ "types-aiofiles==23.2.0.20240106",
68
68
"types-cryptography==3.3.23.2",
69
69
"types-pyyaml==6.0.12.12",
70
-- "types-setuptools==69.0.0.0",
70
++ "types-setuptools==69.0.0.20240115",
71
71
]
72
72

73
73
legacy_keyring_dependencies = [
tests/blockchain/test_blockchain.py CHANGED
@@@ -576,10 -576,10 +576,9 @@@ class TestBlockHeaderValidation
576
576
block.finished_sub_slots[-1],
577
577
"infused_challenge_chain",
578
578
InfusedChallengeChainSubSlot(
579
-- replace(
580
-- block.finished_sub_slots[
581
-- -1
582
-- ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf,
579
++ block.finished_sub_slots[
580
++ -1
581
++ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
583
582
number_of_iterations=uint64(10000000),
584
583
)
585
584
),
@@@ -594,10 -594,10 +593,9 @@@
594
593
block.finished_sub_slots[-1],
595
594
"infused_challenge_chain",
596
595
InfusedChallengeChainSubSlot(
597
-- replace(
598
-- block.finished_sub_slots[
599
-- -1
600
-- ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf,
596
++ block.finished_sub_slots[
597
++ -1
598
++ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
601
599
output=ClassgroupElement.get_default_element(),
602
600
)
603
601
),
@@@ -613,11 -613,11 +611,10 @@@
613
611
block.finished_sub_slots[-1],
614
612
"infused_challenge_chain",
615
613
InfusedChallengeChainSubSlot(
616
-- replace(
617
-- block.finished_sub_slots[
618
-- -1
619
-- ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf,
620
-- challenge=bytes32([0] * 32),
614
++ block.finished_sub_slots[
615
++ -1
616
++ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
617
++ challenge=bytes32([0] * 32)
621
618
)
622
619
),
623
620
)
@@@ -660,8 -660,8 +657,7 @@@
660
657
new_finished_ss = recursive_replace(
661
658
block.finished_sub_slots[-1],
662
659
"challenge_chain",
663
-- replace(
664
-- block.finished_sub_slots[-1].challenge_chain,
660
++ block.finished_sub_slots[-1].challenge_chain.replace(
665
661
infused_challenge_chain_sub_slot_hash=bytes([1] * 32),
666
662
),
667
663
)
@@@ -671,8 -671,8 +667,7 @@@
671
667
new_finished_ss = recursive_replace(
672
668
block.finished_sub_slots[-1],
673
669
"challenge_chain",
674
-- replace(
675
-- block.finished_sub_slots[-1].challenge_chain,
670
++ block.finished_sub_slots[-1].challenge_chain.replace(
676
671
infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[
677
672
-1
678
673
].infused_challenge_chain.get_hash(),
@@@ -698,7 -698,7 +693,7 @@@
698
693
new_finished_ss_bad_rc = recursive_replace(
699
694
block.finished_sub_slots[-1],
700
695
"reward_chain",
701
-- replace(block.finished_sub_slots[-1].reward_chain, infused_challenge_chain_sub_slot_hash=None),
696
++ block.finished_sub_slots[-1].reward_chain.replace(infused_challenge_chain_sub_slot_hash=None),
702
697
)
703
698
block_bad = recursive_replace(
704
699
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
@@@ -746,7 -746,7 +741,7 @@@
746
741
new_finished_ss = recursive_replace(
747
742
blocks[-1].finished_sub_slots[-1],
748
743
"challenge_chain",
749
-- replace(blocks[-1].finished_sub_slots[-1].challenge_chain, subepoch_summary_hash=std_hash(b"0")),
744
++ blocks[-1].finished_sub_slots[-1].challenge_chain.replace(subepoch_summary_hash=std_hash(b"0")),
750
745
)
751
746
block_bad = recursive_replace(
752
747
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
@@@ -794,7 -794,7 +789,7 @@@
794
789
new_finished_ss = recursive_replace(
795
790
blocks[-1].finished_sub_slots[-1],
796
791
"reward_chain",
797
-- replace(blocks[-1].finished_sub_slots[-1].reward_chain, challenge_chain_sub_slot_hash=bytes([3] * 32)),
792
++ blocks[-1].finished_sub_slots[-1].reward_chain.replace(challenge_chain_sub_slot_hash=bytes([3] * 32)),
798
793
)
799
794
block_1_bad = recursive_replace(
800
795
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
@@@ -1040,8 -1040,8 +1035,8 @@@
1040
1035
new_finished_ss = recursive_replace(
1041
1036
new_finished_ss,
1042
1037
"reward_chain",
1043
-- replace(
1044
-- new_finished_ss.reward_chain, challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash()
1038
++ new_finished_ss.reward_chain.replace(
1039
++ challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash()
1045
1040
),
1046
1041
)
1047
1042
block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:])
@@@ -1075,8 -1075,8 +1070,7 @@@
1075
1070
new_finished_ss = recursive_replace(
1076
1071
new_finished_ss,
1077
1072
"reward_chain",
1078
-- replace(
1079
-- new_finished_ss.reward_chain,
1073
++ new_finished_ss.reward_chain.replace(
1080
1074
challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash(),
1081
1075
),
1082
1076
)
tests/blockchain/test_lookup_fork_chain.py CHANGED
@@@ -5,12 -5,12 +5,12 @@@ from typing import Dict, Lis
5
5

6
6
import pytest
7
7

8
-- from benchmarks.utils import rand_hash
9
8
from chia.consensus.block_record import BlockRecord
10
9
from chia.consensus.blockchain_interface import BlockchainInterface
11
10
from chia.consensus.find_fork_point import find_fork_point_in_chain, lookup_fork_chain
12
11
from chia.types.blockchain_format.sized_bytes import bytes32
13
12
from chia.util.ints import uint32
13
++ from tests.util.benchmarks import rand_hash
14
14

15
15

16
16
class DummyChain:
tests/build-job-matrix.py CHANGED
@@@ -129,8 -129,8 +129,8 @@@ for path in test_paths
129
129
# TODO: design a configurable system for this
130
130
process_count = {
131
131
"macos": {False: 0, True: 4}.get(conf["parallel"], conf["parallel"]),
132
-- "ubuntu": {False: 0, True: 4}.get(conf["parallel"], conf["parallel"]),
133
-- "windows": {False: 0, True: 3}.get(conf["parallel"], conf["parallel"]),
132
++ "ubuntu": {False: 0, True: 6}.get(conf["parallel"], conf["parallel"]),
133
++ "windows": {False: 0, True: 6}.get(conf["parallel"], conf["parallel"]),
134
134
}
135
135
pytest_parallel_args = {os: f" -n {count}" for os, count in process_count.items()}
136
136

tests/cmds/wallet/test_did.py CHANGED
@@@ -323,6 -323,6 +323,6 @@@ def test_did_transfer(capsys: object, g
323
323
]
324
324
run_cli_command_and_assert(capsys, root_dir, command_args, assert_list)
325
325
expected_calls: logType = {
326
-- "did_transfer_did": [(w_id, t_address, "0.5", True, DEFAULT_TX_CONFIG.override(reuse_puzhash=True))],
326
++ "did_transfer_did": [(w_id, t_address, 500000000000, True, DEFAULT_TX_CONFIG.override(reuse_puzhash=True))],
327
327
}
328
328
test_rpc_clients.wallet_rpc_client.check_log(expected_calls)
tests/cmds/wallet/test_wallet.py CHANGED
@@@ -3,6 -3,6 +3,7 @@@ from __future__ import annotation
3
3
from pathlib import Path
4
4
from typing import Any, Dict, List, Optional, Tuple, Union, cast
5
5

6
++ import pkg_resources
6
7
from chia_rs import Coin, G2Element
7
8

8
9
from chia.server.outbound_message import NodeType
@@@ -35,7 -35,7 +36,7 @@@ from tests.cmds.wallet.test_consts impo
35
36
get_bytes32,
36
37
)
37
38

38
-- test_offer_file_path: Path = Path("tests") / "cmds" / "wallet" / "test_offer.toffer"
39
++ test_offer_file_path: Path = Path(pkg_resources.resource_filename(__name__, "test_offer.toffer"))
39
40
test_offer_file_name: str = str(test_offer_file_path)
40
41
test_offer_file_bech32: str = open(test_offer_file_name).read()
41
42
test_offer_id: str = "0xdfb7e8643376820ec995b0bcdb3fc1f764c16b814df5e074631263fcf1e00839"
@@@ -696,6 -696,6 +697,7 @@@ def test_make_offer(capsys: object, get
696
697
"Including Fees: 1 XCH, 1000000000000 mojos",
697
698
"Created offer with ID 0202020202020202020202020202020202020202020202020202020202020202",
698
699
]
700
++ run_cli_command_and_assert(capsys, root_dir, command_args[:-4], ["without --override"])
699
701
run_cli_command_and_assert(capsys, root_dir, command_args, assert_list)
700
702
expected_calls: logType = {
701
703
"cat_asset_id_to_name": [(request_cat_id,)],
tests/core/data_layer/test_data_rpc.py CHANGED
@@@ -2292,3 -2292,3 +2292,39 @@@ async def test_wallet_log_in_changes_ac
2292
2292

2293
2293
active_fingerprint = cast(int, (await wallet_rpc_api.get_logged_in_fingerprint(request={}))["fingerprint"])
2294
2294
assert active_fingerprint == secondary_fingerprint
2295
++
2296
++
2297
++ @pytest.mark.limit_consensus_modes(reason="does not depend on consensus rules")
2298
++ @pytest.mark.anyio
2299
++ async def test_mirrors(
2300
++ self_hostname: str, one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices, tmp_path: Path
2301
++ ) -> None:
2302
++ wallet_rpc_api, full_node_api, wallet_rpc_port, ph, bt = await init_wallet_and_node(
2303
++ self_hostname, one_wallet_and_one_simulator_services
2304
++ )
2305
++ async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
2306
++ data_rpc_api = DataLayerRpcApi(data_layer)
2307
++ res = await data_rpc_api.create_data_store({})
2308
++ assert res is not None
2309
++ store_id = bytes32(hexstr_to_bytes(res["id"]))
2310
++ await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
2311
++
2312
++ urls = ["http://127.0.0.1/8000", "http://127.0.0.1/8001"]
2313
++ res = await data_rpc_api.add_mirror({"id": store_id.hex(), "urls": urls, "amount": 1, "fee": 1})
2314
++
2315
++ await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
2316
++ mirrors = await data_rpc_api.get_mirrors({"id": store_id.hex()})
2317
++ mirror_list = mirrors["mirrors"]
2318
++ assert len(mirror_list) == 1
2319
++ mirror = mirror_list[0]
2320
++ assert mirror["urls"] == ["http://127.0.0.1/8000", "http://127.0.0.1/8001"]
2321
++ coin_id = mirror["coin_id"]
2322
++
2323
++ res = await data_rpc_api.delete_mirror({"coin_id": coin_id, "fee": 1})
2324
++ await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
2325
++ mirrors = await data_rpc_api.get_mirrors({"id": store_id.hex()})
2326
++ mirror_list = mirrors["mirrors"]
2327
++ assert len(mirror_list) == 0
2328
++
2329
++ with pytest.raises(RuntimeError, match="URL list can't be empty"):
2330
++ res = await data_rpc_api.add_mirror({"id": store_id.hex(), "urls": [], "amount": 1, "fee": 1})
tests/core/data_layer/test_data_store.py CHANGED
@@@ -308,7 -308,7 +308,7 @@@ async def test_get_ancestors_optimized(
308
308
if i > 25 and i <= 200 and random.randint(0, 4):
309
309
is_insert = True
310
310
if i > 200:
311
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id)
311
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id)
312
312
if not deleted_all:
313
313
while node_count > 0:
314
314
node_count -= 1
@@@ -383,7 -383,7 +383,7 @@@ async def test_batch_update(data_store
383
383

384
384
batch: List[Dict[str, Any]] = []
385
385
keys_values: Dict[bytes, bytes] = {}
386
-- hint_keys_values: Optional[Dict[bytes, bytes]] = {} if use_optimized else None
386
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = {} if use_optimized else None
387
387
for operation in range(num_batches * num_ops_per_batch):
388
388
[op_type] = random.choices(
389
389
["insert", "upsert-insert", "upsert-update", "delete"],
@@@ -490,7 -490,7 +490,7 @@@ async def test_upsert_ignores_existing_
490
490
) -> None:
491
491
key = b"key"
492
492
value = b"value1"
493
-- hint_keys_values: Optional[Dict[bytes, bytes]] = {} if use_optimized else None
493
++ hint_keys_values: Optional[Dict[bytes32, bytes32]] = {} if use_optimized else None
494
494

495
495
await data_store.autoinsert(
496
496
key=key,
@@@ -643,7 -643,7 +643,7 @@@ async def test_inserting_duplicate_key_
643
643
side=Side.RIGHT,
644
644
)
645
645

646
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id=tree_id)
646
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
647
647
# TODO: more specific exception
648
648
with pytest.raises(Exception):
649
649
await data_store.insert(
@@@ -691,7 -691,7 +691,7 @@@ async def test_inserting_invalid_length
691
691
async def test_autoinsert_balances_from_scratch(data_store: DataStore, tree_id: bytes32) -> None:
692
692
random = Random()
693
693
random.seed(100, version=2)
694
-- hint_keys_values: Dict[bytes, bytes] = {}
694
++ hint_keys_values: Dict[bytes32, bytes32] = {}
695
695
hashes = []
696
696

697
697
for i in range(2000):
@@@ -710,7 -710,7 +710,7 @@@
710
710
async def test_autoinsert_balances_gaps(data_store: DataStore, tree_id: bytes32) -> None:
711
711
random = Random()
712
712
random.seed(101, version=2)
713
-- hint_keys_values: Dict[bytes, bytes] = {}
713
++ hint_keys_values: Dict[bytes32, bytes32] = {}
714
714
hashes = []
715
715

716
716
for i in range(2000):
@@@ -749,7 -749,7 +749,7 @@@ async def test_delete_from_left_both_te
749
749

750
750
hint_keys_values = None
751
751
if use_hint:
752
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id=tree_id)
752
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
753
753

754
754
expected = Program.to(
755
755
(
@@@ -789,7 -789,7 +789,7 @@@ async def test_delete_from_left_other_n
789
789

790
790
hint_keys_values = None
791
791
if use_hint:
792
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id=tree_id)
792
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
793
793

794
794
expected = Program.to(
795
795
(
@@@ -827,7 -827,7 +827,7 @@@ async def test_delete_from_right_both_t
827
827

828
828
hint_keys_values = None
829
829
if use_hint:
830
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id=tree_id)
830
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
831
831

832
832
expected = Program.to(
833
833
(
@@@ -867,7 -867,7 +867,7 @@@ async def test_delete_from_right_other_
867
867

868
868
hint_keys_values = None
869
869
if use_hint:
870
-- hint_keys_values = await data_store.get_keys_values_dict(tree_id=tree_id)
870
++ hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
871
871

872
872
expected = Program.to(
873
873
(
@@@ -1208,6 -1208,6 +1208,33 @@@ async def test_kv_diff_2(data_store: Da
1208
1208
assert diff_3 == set()
1209
1209

1210
1210

1211
++ @pytest.mark.anyio
1212
++ async def test_kv_diff_3(data_store: DataStore, tree_id: bytes32) -> None:
1213
++ insert_result = await data_store.autoinsert(
1214
++ key=b"000",
1215
++ value=b"000",
1216
++ tree_id=tree_id,
1217
++ status=Status.COMMITTED,
1218
++ )
1219
++ await data_store.delete(tree_id=tree_id, key=b"000", status=Status.COMMITTED)
1220
++ insert_result_2 = await data_store.autoinsert(
1221
++ key=b"000",
1222
++ value=b"001",
1223
++ tree_id=tree_id,
1224
++ status=Status.COMMITTED,
1225
++ )
1226
++ diff_1 = await data_store.get_kv_diff(tree_id, insert_result.node_hash, insert_result_2.node_hash)
1227
++ assert diff_1 == {DiffData(OperationType.DELETE, b"000", b"000"), DiffData(OperationType.INSERT, b"000", b"001")}
1228
++ insert_result_3 = await data_store.upsert(
1229
++ key=b"000",
1230
++ new_value=b"002",
1231
++ tree_id=tree_id,
1232
++ status=Status.COMMITTED,
1233
++ )
1234
++ diff_2 = await data_store.get_kv_diff(tree_id, insert_result_2.node_hash, insert_result_3.node_hash)
1235
++ assert diff_2 == {DiffData(OperationType.DELETE, b"000", b"001"), DiffData(OperationType.INSERT, b"000", b"002")}
1236
++
1237
++
1211
1238
@pytest.mark.anyio
1212
1239
async def test_rollback_to_generation(data_store: DataStore, tree_id: bytes32) -> None:
1213
1240
await add_0123_example(data_store, tree_id)
tests/core/full_node/stores/test_coin_store.py CHANGED
@@@ -111,7 -111,7 +111,7 @@@ async def test_basic_coin_store(db_vers
111
111
assert block.foliage_transaction_block is not None
112
112
await coin_store.new_block(
113
113
block.height,
114
-- block.foliage_transaction_block.timestamp,
114
++ uint64(block.foliage_transaction_block.timestamp),
115
115
block.get_included_reward_coins(),
116
116
tx_additions,
117
117
tx_removals,
@@@ -121,7 -121,7 +121,7 @@@
121
121
with pytest.raises(Exception):
122
122
await coin_store.new_block(
123
123
block.height,
124
-- block.foliage_transaction_block.timestamp,
124
++ uint64(block.foliage_transaction_block.timestamp),
125
125
block.get_included_reward_coins(),
126
126
tx_additions,
127
127
tx_removals,
@@@ -179,7 -179,7 +179,7 @@@ async def test_set_spent(db_version: in
179
179
assert block.foliage_transaction_block is not None
180
180
await coin_store.new_block(
181
181
block.height,
182
-- block.foliage_transaction_block.timestamp,
182
++ uint64(block.foliage_transaction_block.timestamp),
183
183
block.get_included_reward_coins(),
184
184
additions,
185
185
removals,
@@@ -227,7 -227,7 +227,7 @@@ async def test_num_unspent(bt: BlockToo
227
227
additions: List[Coin] = []
228
228
await coin_store.new_block(
229
229
block.height,
230
-- block.foliage_transaction_block.timestamp,
230
++ uint64(block.foliage_transaction_block.timestamp),
231
231
block.get_included_reward_coins(),
232
232
additions,
233
233
removals,
@@@ -259,7 -259,7 +259,7 @@@ async def test_rollback(db_version: int
259
259
assert block.foliage_transaction_block is not None
260
260
await coin_store.new_block(
261
261
block.height,
262
-- block.foliage_transaction_block.timestamp,
262
++ uint64(block.foliage_transaction_block.timestamp),
263
263
block.get_included_reward_coins(),
264
264
additions,
265
265
removals,
tests/core/full_node/stores/test_full_node_store.py CHANGED
@@@ -114,7 -114,7 +114,9 @@@ async def test_basic_store
114
114
h_hash_1 = bytes32.random(seeded_random)
115
115
assert not store.seen_unfinished_block(h_hash_1)
116
116
assert store.seen_unfinished_block(h_hash_1)
117
-- store.clear_seen_unfinished_blocks()
117
++ # this will crowd out h_hash_1
118
++ for _ in range(store.max_seen_unfinished_blocks):
119
++ store.seen_unfinished_block(bytes32.random(seeded_random))
118
120
assert not store.seen_unfinished_block(h_hash_1)
119
121

120
122
# Add/get unfinished block
@@@ -501,7 -501,7 +503,7 @@@
501
503
blocks[1].reward_chain_sp_proof,
502
504
)
503
505
assert not store.new_signage_point(
504
-- blocks[1].reward_chain_block.signage_point_index,
506
++ uint8(blocks[1].reward_chain_block.signage_point_index),
505
507
blockchain,
506
508
peak,
507
509
uint64(blockchain.block_record(blocks[1].header_hash).sp_sub_slot_total_iters(custom_block_tools.constants)),
@@@ -803,9 -803,9 +805,9 @@@
803
805

804
806
blocks = custom_block_tools.get_consecutive_blocks(2, block_list_input=blocks, guarantee_transaction_block=True)
805
807

806
-- i3 = blocks[-3].reward_chain_block.signage_point_index
807
-- i2 = blocks[-2].reward_chain_block.signage_point_index
808
-- i1 = blocks[-1].reward_chain_block.signage_point_index
808
++ i3 = uint8(blocks[-3].reward_chain_block.signage_point_index)
809
++ i2 = uint8(blocks[-2].reward_chain_block.signage_point_index)
810
++ i1 = uint8(blocks[-1].reward_chain_block.signage_point_index)
809
811
if (
810
812
len(blocks[-2].finished_sub_slots) == len(blocks[-1].finished_sub_slots) == 0
811
813
and not is_overflow_block(custom_block_tools.constants, signage_point_index=i2)
tests/core/full_node/test_full_node.py CHANGED
@@@ -63,8 -63,8 +63,8 @@@ from tests.conftest import ConsensusMod
63
63
from tests.connection_utils import add_dummy_connection, connect_and_get_peer
64
64
from tests.core.full_node.stores.test_coin_store import get_future_reward_coins
65
65
from tests.core.make_block_generator import make_spend_bundle
66
-- from tests.core.mempool.test_mempool_performance import wallet_height_at_least
67
66
from tests.core.node_height import node_height_at_least
67
++ from tests.util.misc import wallet_height_at_least
68
68
from tests.util.setup_nodes import SimulatorsAndWalletsServices
69
69
from tests.util.time_out_assert import time_out_assert, time_out_assert_custom_interval, time_out_messages
70
70

@@@ -1586,12 -1586,12 +1586,10 @@@ class TestFullNodeProtocol
1586
1586
# Submit the sub slot, but not the last block
1587
1587
blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1, force_overflow=True)
1588
1588
for ss in blocks[-1].finished_sub_slots:
1589
-- challenge_chain = dataclasses.replace(
1590
-- ss.challenge_chain,
1589
++ challenge_chain = ss.challenge_chain.replace(
1591
1590
new_difficulty=20,
1592
1591
)
1593
-- slot2 = dataclasses.replace(
1594
-- ss,
1592
++ slot2 = ss.replace(
1595
1593
challenge_chain=challenge_chain,
1596
1594
)
1597
1595
await full_node_1.respond_end_of_sub_slot(fnp.RespondEndOfSubSlot(slot2), peer)
tests/core/full_node/test_subscriptions.py CHANGED
@@@ -20,21 -20,21 +20,21 @@@ ph4 = bytes32(b"h" * 32
20
20
def test_has_ph_sub() -> None:
21
21
sub = PeerSubscriptions()
22
22

23
-- assert sub.has_ph_subscription(ph1) is False
24
-- assert sub.has_ph_subscription(ph2) is False
23
++ assert sub.has_puzzle_subscription(ph1) is False
24
++ assert sub.has_puzzle_subscription(ph2) is False
25
25

26
-- ret = sub.add_ph_subscriptions(peer1, [ph1], 100)
26
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1], 100)
27
27
assert ret == {ph1}
28
28

29
-- assert sub.has_ph_subscription(ph1) is True
30
-- assert sub.has_ph_subscription(ph2) is False
29
++ assert sub.has_puzzle_subscription(ph1) is True
30
++ assert sub.has_puzzle_subscription(ph2) is False
31
31

32
-- ret = sub.add_ph_subscriptions(peer1, [ph1, ph2], 100)
32
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2], 100)
33
33
# we have already subscribed to ph1, it's filtered in the returned list
34
34
assert ret == {ph2}
35
35

36
-- assert sub.has_ph_subscription(ph1) is True
37
-- assert sub.has_ph_subscription(ph2) is True
36
++ assert sub.has_puzzle_subscription(ph1) is True
37
++ assert sub.has_puzzle_subscription(ph2) is True
38
38

39
39
# note that this is technically a type error as well.
40
40
# we can remove these asserts once we have type checking
@@@ -43,8 -43,8 +43,8 @@@
43
43

44
44
sub.remove_peer(peer1)
45
45

46
-- assert sub.has_ph_subscription(ph1) is False
47
-- assert sub.has_ph_subscription(ph2) is False
46
++ assert sub.has_puzzle_subscription(ph1) is False
47
++ assert sub.has_puzzle_subscription(ph2) is False
48
48

49
49

50
50
def test_has_coin_sub() -> None:
@@@ -65,8 -65,8 +65,8 @@@
65
65

66
66
# note that this is technically a type error as well.
67
67
# we can remove these asserts once we have type checking
68
-- assert sub.has_ph_subscription(coin1) is False
69
-- assert sub.has_ph_subscription(coin2) is False
68
++ assert sub.has_puzzle_subscription(coin1) is False
69
++ assert sub.has_puzzle_subscription(coin2) is False
70
70

71
71
sub.remove_peer(peer1)
72
72

@@@ -119,34 -119,34 +119,34 @@@ def test_overlapping_coin_subscriptions
119
119
def test_overlapping_ph_subscriptions() -> None:
120
120
sub = PeerSubscriptions()
121
121

122
-- assert sub.has_ph_subscription(ph1) is False
123
-- assert sub.has_ph_subscription(ph2) is False
122
++ assert sub.has_puzzle_subscription(ph1) is False
123
++ assert sub.has_puzzle_subscription(ph2) is False
124
124

125
125
assert sub.peers_for_puzzle_hash(ph1) == set()
126
126
assert sub.peers_for_puzzle_hash(ph2) == set()
127
127

128
128
# subscribed to different phs
129
-- ret = sub.add_ph_subscriptions(peer1, [ph1], 100)
129
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1], 100)
130
130
assert ret == {ph1}
131
131

132
132
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
133
133
assert sub.peers_for_puzzle_hash(ph2) == set()
134
134

135
-- ret = sub.add_ph_subscriptions(peer2, [ph2], 100)
135
++ ret = sub.add_puzzle_subscriptions(peer2, [ph2], 100)
136
136
assert ret == {ph2}
137
137

138
-- assert sub.has_ph_subscription(ph1) is True
139
-- assert sub.has_ph_subscription(ph2) is True
138
++ assert sub.has_puzzle_subscription(ph1) is True
139
++ assert sub.has_puzzle_subscription(ph2) is True
140
140

141
141
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
142
142
assert sub.peers_for_puzzle_hash(ph2) == {peer2}
143
143

144
144
# peer1 is now subscribing to both phs
145
-- ret = sub.add_ph_subscriptions(peer1, [ph2], 100)
145
++ ret = sub.add_puzzle_subscriptions(peer1, [ph2], 100)
146
146
assert ret == {ph2}
147
147

148
-- assert sub.has_ph_subscription(ph1) is True
149
-- assert sub.has_ph_subscription(ph2) is True
148
++ assert sub.has_puzzle_subscription(ph1) is True
149
++ assert sub.has_puzzle_subscription(ph2) is True
150
150

151
151
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
152
152
assert sub.peers_for_puzzle_hash(ph2) == {peer1, peer2}
@@@ -154,8 -154,8 +154,8 @@@
154
154
# removing peer1 still leaves the subscription to ph2
155
155
sub.remove_peer(peer1)
156
156

157
-- assert sub.has_ph_subscription(ph1) is False
158
-- assert sub.has_ph_subscription(ph2) is True
157
++ assert sub.has_puzzle_subscription(ph1) is False
158
++ assert sub.has_puzzle_subscription(ph2) is True
159
159

160
160
assert sub.peers_for_puzzle_hash(ph1) == set()
161
161
assert sub.peers_for_puzzle_hash(ph2) == {peer2}
@@@ -164,19 -164,19 +164,19 @@@
164
164
def test_ph_sub_limit() -> None:
165
165
sub = PeerSubscriptions()
166
166

167
-- assert sub.has_ph_subscription(ph1) is False
168
-- assert sub.has_ph_subscription(ph2) is False
169
-- assert sub.has_ph_subscription(ph2) is False
170
-- assert sub.has_ph_subscription(ph3) is False
167
++ assert sub.has_puzzle_subscription(ph1) is False
168
++ assert sub.has_puzzle_subscription(ph2) is False
169
++ assert sub.has_puzzle_subscription(ph2) is False
170
++ assert sub.has_puzzle_subscription(ph3) is False
171
171

172
-- ret = sub.add_ph_subscriptions(peer1, [ph1, ph2, ph3, ph4], 3)
172
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4], 3)
173
173
# we only ended up subscribing to 3 puzzle hashes because of the limit
174
174
assert ret == {ph1, ph2, ph3}
175
175

176
-- assert sub.has_ph_subscription(ph1) is True
177
-- assert sub.has_ph_subscription(ph2) is True
178
-- assert sub.has_ph_subscription(ph3) is True
179
-- assert sub.has_ph_subscription(ph4) is False
176
++ assert sub.has_puzzle_subscription(ph1) is True
177
++ assert sub.has_puzzle_subscription(ph2) is True
178
++ assert sub.has_puzzle_subscription(ph3) is True
179
++ assert sub.has_puzzle_subscription(ph4) is False
180
180

181
181
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
182
182
assert sub.peers_for_puzzle_hash(ph2) == {peer1}
@@@ -184,10 -184,10 +184,10 @@@
184
184
assert sub.peers_for_puzzle_hash(ph4) == set()
185
185

186
186
# peer1 should still be limited
187
-- ret = sub.add_ph_subscriptions(peer1, [ph4], 3)
187
++ ret = sub.add_puzzle_subscriptions(peer1, [ph4], 3)
188
188
assert ret == set()
189
189

190
-- assert sub.has_ph_subscription(ph4) is False
190
++ assert sub.has_puzzle_subscription(ph4) is False
191
191
assert sub.peers_for_puzzle_hash(ph4) == set()
192
192

193
193
# peer1 is also limied on coin subscriptions
@@@ -197,10 -197,10 +197,10 @@@
197
197
assert sub.peers_for_coin_id(coin1) == set()
198
198

199
199
# peer2 is has its own limit
200
-- ret = sub.add_ph_subscriptions(peer2, [ph4], 3)
200
++ ret = sub.add_puzzle_subscriptions(peer2, [ph4], 3)
201
201
assert ret == {ph4}
202
202

203
-- assert sub.has_ph_subscription(ph4) is True
203
++ assert sub.has_puzzle_subscription(ph4) is True
204
204
assert sub.peers_for_puzzle_hash(ph4) == {peer2}
205
205

206
206
sub.remove_peer(peer1)
@@@ -210,18 -210,18 +210,18 @@@
210
210
def test_ph_sub_limit_incremental() -> None:
211
211
sub = PeerSubscriptions()
212
212

213
-- assert sub.has_ph_subscription(ph1) is False
214
-- assert sub.has_ph_subscription(ph2) is False
215
-- assert sub.has_ph_subscription(ph2) is False
216
-- assert sub.has_ph_subscription(ph3) is False
213
++ assert sub.has_puzzle_subscription(ph1) is False
214
++ assert sub.has_puzzle_subscription(ph2) is False
215
++ assert sub.has_puzzle_subscription(ph2) is False
216
++ assert sub.has_puzzle_subscription(ph3) is False
217
217

218
-- ret = sub.add_ph_subscriptions(peer1, [ph1], 2)
218
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1], 2)
219
219
assert ret == {ph1}
220
220

221
-- assert sub.has_ph_subscription(ph1) is True
222
-- assert sub.has_ph_subscription(ph2) is False
223
-- assert sub.has_ph_subscription(ph3) is False
224
-- assert sub.has_ph_subscription(ph4) is False
221
++ assert sub.has_puzzle_subscription(ph1) is True
222
++ assert sub.has_puzzle_subscription(ph2) is False
223
++ assert sub.has_puzzle_subscription(ph3) is False
224
++ assert sub.has_puzzle_subscription(ph4) is False
225
225

226
226
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
227
227
assert sub.peers_for_puzzle_hash(ph2) == set()
@@@ -229,13 -229,13 +229,13 @@@
229
229
assert sub.peers_for_puzzle_hash(ph4) == set()
230
230

231
231
# this will cross the limit. Only ph2 will be added
232
-- ret = sub.add_ph_subscriptions(peer1, [ph2, ph3], 2)
232
++ ret = sub.add_puzzle_subscriptions(peer1, [ph2, ph3], 2)
233
233
assert ret == {ph2}
234
234

235
-- assert sub.has_ph_subscription(ph1) is True
236
-- assert sub.has_ph_subscription(ph2) is True
237
-- assert sub.has_ph_subscription(ph3) is False
238
-- assert sub.has_ph_subscription(ph4) is False
235
++ assert sub.has_puzzle_subscription(ph1) is True
236
++ assert sub.has_puzzle_subscription(ph2) is True
237
++ assert sub.has_puzzle_subscription(ph3) is False
238
++ assert sub.has_puzzle_subscription(ph4) is False
239
239

240
240
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
241
241
assert sub.peers_for_puzzle_hash(ph2) == {peer1}
@@@ -272,10 -272,10 +272,10 @@@ def test_coin_sub_limit() -> None
272
272
assert sub.peers_for_coin_id(coin4) == set()
273
273

274
274
# peer1 is also limied on ph subscriptions
275
-- ret = sub.add_ph_subscriptions(peer1, [ph1], 3)
275
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1], 3)
276
276
assert ret == set()
277
277

278
-- assert sub.has_ph_subscription(ph1) is False
278
++ assert sub.has_puzzle_subscription(ph1) is False
279
279
assert sub.peers_for_puzzle_hash(ph1) == set()
280
280

281
281
# peer2 is has its own limit
@@@ -327,31 -327,31 +327,77 @@@ def test_coin_sub_limit_incremental() -
327
327
def test_ph_subscription_duplicates() -> None:
328
328
sub = PeerSubscriptions()
329
329

330
-- assert sub.has_ph_subscription(ph1) is False
331
-- assert sub.has_ph_subscription(ph2) is False
332
-- assert sub.has_ph_subscription(ph3) is False
333
-- assert sub.has_ph_subscription(ph4) is False
330
++ assert sub.has_puzzle_subscription(ph1) is False
331
++ assert sub.has_puzzle_subscription(ph2) is False
332
++ assert sub.has_puzzle_subscription(ph3) is False
333
++ assert sub.has_puzzle_subscription(ph4) is False
334
334

335
-- ret = sub.add_ph_subscriptions(peer1, [ph1, ph2, ph3], 100)
335
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3], 100)
336
336
assert ret == {ph1, ph2, ph3}
337
337

338
-- assert sub.has_ph_subscription(ph1) is True
339
-- assert sub.has_ph_subscription(ph2) is True
340
-- assert sub.has_ph_subscription(ph3) is True
341
-- assert sub.has_ph_subscription(ph4) is False
338
++ assert sub.has_puzzle_subscription(ph1) is True
339
++ assert sub.has_puzzle_subscription(ph2) is True
340
++ assert sub.has_puzzle_subscription(ph3) is True
341
++ assert sub.has_puzzle_subscription(ph4) is False
342
342

343
343
# only ph4 is new, the others are duplicates and ignored
344
-- ret = sub.add_ph_subscriptions(peer1, [ph1, ph2, ph3, ph4], 100)
344
++ ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4], 100)
345
345
assert ret == {ph4}
346
346

347
-- assert sub.has_ph_subscription(ph1) is True
348
-- assert sub.has_ph_subscription(ph2) is True
349
-- assert sub.has_ph_subscription(ph3) is True
350
-- assert sub.has_ph_subscription(ph4) is True
347
++ assert sub.has_puzzle_subscription(ph1) is True
348
++ assert sub.has_puzzle_subscription(ph2) is True
349
++ assert sub.has_puzzle_subscription(ph3) is True
350
++ assert sub.has_puzzle_subscription(ph4) is True
351
351

352
352
sub.remove_peer(peer1)
353
353

354
-- assert sub.has_ph_subscription(ph1) is False
355
-- assert sub.has_ph_subscription(ph2) is False
356
-- assert sub.has_ph_subscription(ph3) is False
357
-- assert sub.has_ph_subscription(ph4) is False
354
++ assert sub.has_puzzle_subscription(ph1) is False
355
++ assert sub.has_puzzle_subscription(ph2) is False
356
++ assert sub.has_puzzle_subscription(ph3) is False
357
++ assert sub.has_puzzle_subscription(ph4) is False
358
++
359
++
360
++ def test_remove_ph_subscriptions() -> None:
361
++ sub = PeerSubscriptions()
362
++
363
++ added = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4, ph4], 100)
364
++ assert added == {ph1, ph2, ph3, ph4}
365
++
366
++ removed = sub.remove_puzzle_subscriptions(peer1, list(added))
367
++ assert removed == added
368
++
369
++ # These have already been removed.
370
++ assert len(sub.remove_puzzle_subscriptions(peer1, [ph1, ph2])) == 0
371
++
372
++ assert sub.peer_subscription_count(peer1) == 0
373
++
374
++ for ph in removed:
375
++ assert not sub.has_puzzle_subscription(ph)
376
++
377
++
378
++ def test_remove_coin_subscriptions() -> None:
379
++ sub = PeerSubscriptions()
380
++
381
++ added = sub.add_coin_subscriptions(peer1, [coin1, coin2, coin3, coin4, coin4], 100)
382
++ assert added == {coin1, coin2, coin3, coin4}
383
++
384
++ removed = sub.remove_coin_subscriptions(peer1, list(added))
385
++ assert removed == added
386
++
387
++ # These have already been removed.
388
++ assert len(sub.remove_coin_subscriptions(peer1, [coin1, coin2])) == 0
389
++
390
++ assert sub.peer_subscription_count(peer1) == 0
391
++
392
++ for coin_id in removed:
393
++ assert not sub.has_coin_subscription(coin_id)
394
++
395
++
396
++ def test_subscription_list() -> None:
397
++ sub = PeerSubscriptions()
398
++
399
++ sub.add_coin_subscriptions(peer1, [coin1, coin2], 4)
400
++ sub.add_puzzle_subscriptions(peer1, [ph1, ph2], 4)
401
++
402
++ assert sub.coin_subscriptions(peer1) == {coin1, coin2}
403
++ assert sub.puzzle_subscriptions(peer1) == {ph1, ph2}
tests/core/mempool/test_mempool_performance.py CHANGED
@@@ -7,19 -7,19 +7,14 @@@ import pytes
7
7
from chia.types.full_block import FullBlock
8
8
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
9
9
from chia.types.peer_info import PeerInfo
10
-- from chia.util.ints import uint32, uint64, uint128
10
++ from chia.util.ints import uint64, uint128
11
11
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
12
12
from chia.wallet.wallet_node import WalletNode
13
-- from tests.util.misc import BenchmarkRunner
13
++ from tests.util.misc import BenchmarkRunner, wallet_height_at_least
14
14
from tests.util.setup_nodes import OldSimulatorsAndWallets
15
15
from tests.util.time_out_assert import time_out_assert
16
16

17
17

18
-- async def wallet_height_at_least(wallet_node: WalletNode, h: uint32) -> bool:
19
-- height = await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()
20
-- return height == h
21
--
22
--
23
18
async def wallet_balance_at_least(wallet_node: WalletNode, balance: uint128) -> bool:
24
19
b = await wallet_node.wallet_state_manager.get_confirmed_balance_for_wallet(1)
25
20
return b >= balance
tests/core/server/test_loop.py CHANGED
@@@ -14,6 -14,6 +14,7 @@@ from typing import AsyncIterator, List
14
14
import anyio
15
15
import pytest
16
16

17
++ import tests
17
18
from chia.server import chia_policy
18
19
from chia.util.timing import adjusted_timeout
19
20
from tests.core.server import serve
@@@ -160,9 -160,9 +161,12 @@@ async def test_loop(tmp_path: pathlib.P
160
161
flood_file = tmp_path.joinpath("flood")
161
162
flood_file.touch()
162
163

164
++ env = {**os.environ, "PYTHONPATH": pathlib.Path(tests.__file__).parent.parent.as_posix()}
165
++
163
166
logger.info(" ==== launching serve.py")
164
167
with subprocess.Popen(
165
168
[sys.executable, "-m", "tests.core.server.serve", os.fspath(serve_file)],
169
++ env=env,
166
170
):
167
171
logger.info(" ==== serve.py running")
168
172

@@@ -171,6 -171,6 +175,7 @@@
171
175
logger.info(" ==== launching flood.py")
172
176
with subprocess.Popen(
173
177
[sys.executable, "-m", "tests.core.server.flood", os.fspath(flood_file)],
178
++ env=env,
174
179
):
175
180
logger.info(" ==== flood.py running")
176
181

tests/core/test_coins.py CHANGED
@@@ -2,10 -2,10 +2,10 @@@ from __future__ import annotation
2
2

3
3
from itertools import permutations
4
4

5
-- from benchmarks.utils import rand_hash
6
5
from chia.types.blockchain_format.coin import hash_coin_ids
7
6
from chia.types.blockchain_format.sized_bytes import bytes32
8
7
from chia.util.hash import std_hash
8
++ from tests.util.benchmarks import rand_hash
9
9

10
10

11
11
def test_hash_coin_ids_empty() -> None:
tests/core/test_full_node_rpc.py CHANGED
@@@ -631,6 -631,6 +631,10 @@@ async def test_get_blockchain_state(one
631
631
# When supplying genesis block, there are no older blocks so `None` should be returned
632
632
assert await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[0], 4608) is None
633
633
assert await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[-1], 4608) is not None
634
++ # Test that get_aggsig_additional_data() returns correctly
635
++ assert (
636
++ full_node_api_1.full_node.constants.AGG_SIG_ME_ADDITIONAL_DATA == await client.get_aggsig_additional_data()
637
++ )
634
638

635
639
finally:
636
640
# Checks that the RPC manages to stop the node
tests/core/util/test_keychain.py CHANGED
@@@ -1,15 -1,15 +1,16 @@@
1
1
from __future__ import annotations
2
2

3
3
import json
4
-- import pathlib
5
4
import random
6
5
from dataclasses import replace
7
6
from typing import Callable, List, Optional, Tuple
8
7

8
++ import pkg_resources
9
9
import pytest
10
10
from chia_rs import AugSchemeMPL, G1Element, PrivateKey
11
11

12
12
import tests
13
++ import tests.util
13
14
from chia.simulator.keyring import TempKeyring
14
15
from chia.types.blockchain_format.sized_bytes import bytes32
15
16
from chia.util.errors import (
@@@ -156,7 -156,7 +157,8 @@@ class TestKeychain
156
157
assert child_sk == PrivateKey.from_bytes(tv_child_int.to_bytes(32, "big"))
157
158

158
159
def test_bip39_test_vectors(self):
159
-- with open("tests/util/bip39_test_vectors.json") as f:
160
++ test_vectors_path = pkg_resources.resource_filename(tests.util.__name__, "bip39_test_vectors.json")
161
++ with open(test_vectors_path) as f:
160
162
all_vectors = json.loads(f.read())
161
163

162
164
for vector_list in all_vectors["english"]:
@@@ -172,7 -172,7 +174,7 @@@
172
174
"""
173
175
Tests that the first 4 letters of each mnemonic phrase matches as if it were the full phrase
174
176
"""
175
-- test_vectors_path = pathlib.Path(tests.__file__).parent.joinpath("util", "bip39_test_vectors.json")
177
++ test_vectors_path = pkg_resources.resource_filename(tests.util.__name__, "bip39_test_vectors.json")
176
178
with open(test_vectors_path) as f:
177
179
all_vectors = json.load(f)
178
180

tests/core/util/test_streamable.py CHANGED
@@@ -555,7 -555,7 +555,7 @@@ def test_recursive_types() -> None
555
555

556
556

557
557
def test_ambiguous_deserialization_optionals() -> None:
558
-- with pytest.raises(AssertionError):
558
++ with pytest.raises(ValueError, match="unexpected end of buffer"):
559
559
SubEpochChallengeSegment.from_bytes(b"\x00\x00\x00\x03\xff\xff\xff\xff")
560
560

561
561
@streamable
tests/pools/test_pool_wallet.py CHANGED
@@@ -8,9 -8,9 +8,9 @@@ from unittest.mock import MagicMoc
8
8
import pytest
9
9
from chia_rs import G1Element
10
10

11
-- from benchmarks.utils import rand_g1, rand_hash
12
11
from chia.pools.pool_wallet import PoolWallet
13
12
from chia.types.blockchain_format.sized_bytes import bytes32
13
++ from tests.util.benchmarks import rand_g1, rand_hash
14
14

15
15

16
16
@dataclass
tests/tools/test_full_sync.py CHANGED
@@@ -8,7 -8,7 +8,7 @@@ from pathlib import Pat
8
8

9
9
import pytest
10
10

11
-- from tools.test_full_sync import run_sync_test
11
++ from tests.util.full_sync import run_sync_test
12
12

13
13

14
14
@pytest.mark.parametrize("keep_up", [True, False])
tests/tools/test_legacy_keyring.py CHANGED
@@@ -12,7 -12,7 +12,7 @@@ except ImportError
12
12
if sys.platform == "linux":
13
13
raise
14
14

15
-- from tools.legacy_keyring import create_legacy_keyring, generate_and_add, get_keys, legacy_keyring
15
++ from chia.legacy.keyring import create_legacy_keyring, generate_and_add, get_keys, legacy_keyring
16
16

17
17

18
18
def show() -> Result:
tests/tools/test_run_block.py CHANGED
@@@ -10,7 -10,7 +10,7 @@@ from chia.types.blockchain_format.sized
10
10
from chia.types.condition_opcodes import ConditionOpcode
11
11
from chia.types.condition_with_args import ConditionWithArgs
12
12
from chia.util.ints import uint32, uint64, uint128
13
-- from tools.run_block import run_json_block
13
++ from tests.util.run_block import run_json_block
14
14

15
15
constants = dataclasses.replace(
16
16
DEFAULT_CONSTANTS,
tests/util/benchmark_cost.py ADDED
@@@ -42,7 -42,7 +42,7 @@@ def run_and_return_cost_time(chialisp)
42
42
clvm_loop_solution = f"(1000 {chialisp})"
43
43
solution_program = Program.to(binutils.assemble(clvm_loop_solution))
44
44

45
-- cost, sexp = loop_program.run_with_cost(solution_program, INFINITE_COST)
45
++ cost, _ = loop_program.run_with_cost(INFINITE_COST, solution_program)
46
46

47
47
end = time.time()
48
48
total_time = end - start
@@@ -135,7 -135,7 +135,7 @@@ if __name__ == "__main__"
135
135
puzzle_start = time.time()
136
136
clvm_cost = 0
137
137
for i in range(0, 1000):
138
-- cost_run, sexp = puzzles[i].run_with_cost(solutions[i], INFINITE_COST)
138
++ cost_run, _ = puzzles[i].run_with_cost(INFINITE_COST, solutions[i])
139
139
clvm_cost += cost_run
140
140

141
141
puzzle_end = time.time()
tests/util/benchmarks.py RENAMED
@@@ -1,0 -1,0 +1,155 @@@
1
++ from __future__ import annotations
2
++
3
++ import random
4
++ from typing import Tuple
5
++
6
++ import pkg_resources
7
++ from chia_rs import AugSchemeMPL, ClassgroupElement, Coin, G1Element, G2Element, VDFInfo, VDFProof
8
++
9
++ from chia.consensus.coinbase import create_farmer_coin, create_pool_coin
10
++ from chia.consensus.default_constants import DEFAULT_CONSTANTS
11
++ from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
12
++ from chia.types.blockchain_format.pool_target import PoolTarget
13
++ from chia.types.blockchain_format.proof_of_space import ProofOfSpace
14
++ from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
15
++ from chia.types.blockchain_format.serialized_program import SerializedProgram
16
++ from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
17
++ from chia.types.full_block import FullBlock
18
++ from chia.util.ints import uint8, uint32, uint64, uint128
19
++
20
++ # farmer puzzle hash
21
++ ph = bytes32(b"a" * 32)
22
++
23
++ clvm_generator_bin_path = pkg_resources.resource_filename(__name__, "clvm_generator.bin")
24
++ with open(clvm_generator_bin_path, "rb") as f:
25
++ clvm_generator = f.read()
26
++
27
++
28
++ def rewards(height: uint32) -> Tuple[Coin, Coin]:
29
++ farmer_coin = create_farmer_coin(height, ph, uint64(250000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
30
++ pool_coin = create_pool_coin(height, ph, uint64(1750000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
31
++ return farmer_coin, pool_coin
32
++
33
++
34
++ def rand_bytes(num: int) -> bytes:
35
++ ret = bytearray(num)
36
++ for i in range(num):
37
++ ret[i] = random.getrandbits(8)
38
++ return bytes(ret)
39
++
40
++
41
++ def rand_hash() -> bytes32:
42
++ return bytes32(rand_bytes(32))
43
++
44
++
45
++ def rand_g1() -> G1Element:
46
++ sk = AugSchemeMPL.key_gen(rand_bytes(96))
47
++ return sk.get_g1()
48
++
49
++
50
++ def rand_g2() -> G2Element:
51
++ sk = AugSchemeMPL.key_gen(rand_bytes(96))
52
++ return AugSchemeMPL.sign(sk, b"foobar")
53
++
54
++
55
++ def rand_class_group_element() -> ClassgroupElement:
56
++ return ClassgroupElement(bytes100(rand_bytes(100)))
57
++
58
++
59
++ def rand_vdf() -> VDFInfo:
60
++ return VDFInfo(rand_hash(), uint64(random.randint(100000, 1000000000)), rand_class_group_element())
61
++
62
++
63
++ def rand_vdf_proof() -> VDFProof:
64
++ return VDFProof(
65
++ uint8(1), # witness_type
66
++ rand_hash(), # witness
67
++ bool(random.randint(0, 1)), # normalized_to_identity
68
++ )
69
++
70
++
71
++ def rand_full_block() -> FullBlock:
72
++ proof_of_space = ProofOfSpace(
73
++ rand_hash(),
74
++ rand_g1(),
75
++ None,
76
++ rand_g1(),
77
++ uint8(0),
78
++ rand_bytes(8 * 32),
79
++ )
80
++
81
++ reward_chain_block = RewardChainBlock(
82
++ uint128(1),
83
++ uint32(2),
84
++ uint128(3),
85
++ uint8(4),
86
++ rand_hash(),
87
++ proof_of_space,
88
++ None,
89
++ rand_g2(),
90
++ rand_vdf(),
91
++ None,
92
++ rand_g2(),
93
++ rand_vdf(),
94
++ rand_vdf(),
95
++ True,
96
++ )
97
++
98
++ pool_target = PoolTarget(
99
++ rand_hash(),
100
++ uint32(0),
101
++ )
102
++
103
++ foliage_block_data = FoliageBlockData(
104
++ rand_hash(),
105
++ pool_target,
106
++ rand_g2(),
107
++ rand_hash(),
108
++ rand_hash(),
109
++ )
110
++
111
++ foliage = Foliage(
112
++ rand_hash(),
113
++ rand_hash(),
114
++ foliage_block_data,
115
++ rand_g2(),
116
++ rand_hash(),
117
++ rand_g2(),
118
++ )
119
++
120
++ foliage_transaction_block = FoliageTransactionBlock(
121
++ rand_hash(),
122
++ uint64(0),
123
++ rand_hash(),
124
++ rand_hash(),
125
++ rand_hash(),
126
++ rand_hash(),
127
++ )
128
++
129
++ farmer_coin, pool_coin = rewards(uint32(0))
130
++
131
++ transactions_info = TransactionsInfo(
132
++ rand_hash(),
133
++ rand_hash(),
134
++ rand_g2(),
135
++ uint64(0),
136
++ uint64(1),
137
++ [farmer_coin, pool_coin],
138
++ )
139
++
140
++ full_block = FullBlock(
141
++ [],
142
++ reward_chain_block,
143
++ rand_vdf_proof(),
144
++ rand_vdf_proof(),
145
++ rand_vdf_proof(),
146
++ rand_vdf_proof(),
147
++ rand_vdf_proof(),
148
++ foliage,
149
++ foliage_transaction_block,
150
++ transactions_info,
151
++ SerializedProgram.from_bytes(clvm_generator),
152
++ [],
153
++ )
154
++
155
++ return full_block
tests/util/full_sync.py RENAMED
@@@ -1,0 -1,0 +1,226 @@@
1
++ from __future__ import annotations
2
++
3
++ import cProfile
4
++ import logging
5
++ import shutil
6
++ import tempfile
7
++ import time
8
++ from contextlib import contextmanager
9
++ from pathlib import Path
10
++ from typing import Iterator, List, Optional, cast
11
++
12
++ import aiosqlite
13
++ import zstd
14
++
15
++ from chia.cmds.init_funcs import chia_init
16
++ from chia.consensus.default_constants import DEFAULT_CONSTANTS
17
++ from chia.full_node.full_node import FullNode
18
++ from chia.server.outbound_message import Message, NodeType
19
++ from chia.server.server import ChiaServer
20
++ from chia.server.ws_connection import ConnectionCallback, WSChiaConnection
21
++ from chia.simulator.block_tools import make_unfinished_block
22
++ from chia.types.blockchain_format.sized_bytes import bytes32
23
++ from chia.types.full_block import FullBlock
24
++ from chia.types.peer_info import PeerInfo
25
++ from chia.util.config import load_config
26
++ from chia.util.ints import uint16
27
++ from tests.util.constants import test_constants as TEST_CONSTANTS
28
++
29
++
30
++ class ExitOnError(logging.Handler):
31
++ def __init__(self) -> None:
32
++ super().__init__()
33
++ self.exit_with_failure = False
34
++
35
++ def emit(self, record: logging.LogRecord) -> None:
36
++ if record.levelno != logging.ERROR:
37
++ return
38
++ self.exit_with_failure = True
39
++
40
++
41
++ @contextmanager
42
++ def enable_profiler(profile: bool, counter: int) -> Iterator[None]:
43
++ if not profile:
44
++ yield
45
++ return
46
++
47
++ with cProfile.Profile() as pr:
48
++ receive_start_time = time.monotonic()
49
++ yield
50
++
51
++ if time.monotonic() - receive_start_time > 5:
52
++ pr.create_stats()
53
++ pr.dump_stats(f"slow-batch-{counter:05d}.profile")
54
++
55
++
56
++ class FakeServer:
57
++ async def send_to_all(
58
++ self, messages: List[Message], node_type: NodeType, exclude: Optional[bytes32] = None
59
++ ) -> None:
60
++ pass
61
++
62
++ def set_received_message_callback(self, callback: ConnectionCallback) -> None:
63
++ pass
64
++
65
++ async def get_peer_info(self) -> Optional[PeerInfo]:
66
++ return None
67
++
68
++ def get_connections(
69
++ self, node_type: Optional[NodeType] = None, *, outbound: Optional[bool] = False
70
++ ) -> List[WSChiaConnection]:
71
++ return []
72
++
73
++ def is_duplicate_or_self_connection(self, target_node: PeerInfo) -> bool:
74
++ return False
75
++
76
++ async def start_client(
77
++ self,
78
++ target_node: PeerInfo,
79
++ on_connect: Optional[ConnectionCallback] = None,
80
++ auth: bool = False,
81
++ is_feeler: bool = False,
82
++ ) -> bool:
83
++ return False
84
++
85
++
86
++ class FakePeer:
87
++ def get_peer_logging(self) -> PeerInfo:
88
++ return PeerInfo("0.0.0.0", uint16(0))
89
++
90
++ def __init__(self) -> None:
91
++ self.peer_node_id = bytes([0] * 32)
92
++
93
++ async def get_peer_info(self) -> Optional[PeerInfo]:
94
++ return None
95
++
96
++
97
++ async def run_sync_test(
98
++ file: Path,
99
++ db_version: int,
100
++ profile: bool,
101
++ single_thread: bool,
102
++ test_constants: bool,
103
++ keep_up: bool,
104
++ db_sync: str,
105
++ node_profiler: bool,
106
++ start_at_checkpoint: Optional[str],
107
++ ) -> None:
108
++ logger = logging.getLogger()
109
++ logger.setLevel(logging.WARNING)
110
++ handler = logging.FileHandler("test-full-sync.log")
111
++ handler.setFormatter(
112
++ logging.Formatter(
113
++ "%(levelname)-8s %(message)s",
114
++ datefmt="%Y-%m-%dT%H:%M:%S",
115
++ )
116
++ )
117
++ logger.addHandler(handler)
118
++ check_log = ExitOnError()
119
++ logger.addHandler(check_log)
120
++
121
++ with tempfile.TemporaryDirectory() as root_dir:
122
++ root_path = Path(root_dir, "root")
123
++ if start_at_checkpoint is not None:
124
++ shutil.copytree(start_at_checkpoint, root_path)
125
++
126
++ chia_init(root_path, should_check_keys=False, v1_db=(db_version == 1))
127
++ config = load_config(root_path, "config.yaml")
128
++
129
++ if test_constants:
130
++ constants = TEST_CONSTANTS
131
++ else:
132
++ overrides = config["network_overrides"]["constants"][config["selected_network"]]
133
++ constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
134
++ if single_thread:
135
++ config["full_node"]["single_threaded"] = True
136
++ config["full_node"]["db_sync"] = db_sync
137
++ config["full_node"]["enable_profiler"] = node_profiler
138
++ full_node = await FullNode.create(
139
++ config["full_node"],
140
++ root_path=root_path,
141
++ consensus_constants=constants,
142
++ )
143
++
144
++ full_node.set_server(cast(ChiaServer, FakeServer()))
145
++ async with full_node.manage():
146
++ peak = full_node.blockchain.get_peak()
147
++ if peak is not None:
148
++ height = int(peak.height)
149
++ else:
150
++ height = 0
151
++
152
++ peer: WSChiaConnection = cast(WSChiaConnection, FakePeer())
153
++
154
++ print()
155
++ counter = 0
156
++ monotonic = height
157
++ prev_hash = None
158
++ async with aiosqlite.connect(file) as in_db:
159
++ await in_db.execute("pragma query_only")
160
++ rows = await in_db.execute(
161
++ "SELECT header_hash, height, block FROM full_blocks "
162
++ "WHERE height >= ? AND in_main_chain=1 ORDER BY height",
163
++ (height,),
164
++ )
165
++
166
++ block_batch = []
167
++
168
++ start_time = time.monotonic()
169
++ logger.warning(f"starting test {start_time}")
170
++ worst_batch_height = None
171
++ worst_batch_time_per_block = None
172
++ peer_info = peer.get_peer_logging()
173
++ async for r in rows:
174
++ batch_start_time = time.monotonic()
175
++ with enable_profiler(profile, height):
176
++ block = FullBlock.from_bytes(zstd.decompress(r[2]))
177
++ block_batch.append(block)
178
++
179
++ assert block.height == monotonic
180
++ monotonic += 1
181
++ assert prev_hash is None or block.prev_header_hash == prev_hash
182
++ prev_hash = block.header_hash
183
++
184
++ if len(block_batch) < 32:
185
++ continue
186
++
187
++ if keep_up:
188
++ for b in block_batch:
189
++ await full_node.add_unfinished_block(make_unfinished_block(b, constants), peer)
190
++ await full_node.add_block(b)
191
++ else:
192
++ success, summary, _ = await full_node.add_block_batch(block_batch, peer_info, None)
193
++ end_height = block_batch[-1].height
194
++ full_node.blockchain.clean_block_record(end_height - full_node.constants.BLOCKS_CACHE_SIZE)
195
++
196
++ if not success:
197
++ raise RuntimeError("failed to ingest block batch")
198
++
199
++ assert summary is not None
200
++
201
++ time_per_block = (time.monotonic() - batch_start_time) / len(block_batch)
202
++ if not worst_batch_height or worst_batch_time_per_block > time_per_block:
203
++ worst_batch_height = height
204
++ worst_batch_time_per_block = time_per_block
205
++
206
++ counter += len(block_batch)
207
++ height += len(block_batch)
208
++ print(
209
++ f"\rheight {height} {time_per_block:0.2f} s/block ",
210
++ end="",
211
++ )
212
++ block_batch = []
213
++ if check_log.exit_with_failure:
214
++ raise RuntimeError("error printed to log. exiting")
215
++
216
++ if counter >= 100000:
217
++ counter = 0
218
++ print()
219
++ end_time = time.monotonic()
220
++ logger.warning(f"test completed at {end_time}")
221
++ logger.warning(f"duration: {end_time - start_time:0.2f} s")
222
++ logger.warning(f"worst time-per-block: {worst_batch_time_per_block:0.2f} s")
223
++ logger.warning(f"worst height: {worst_batch_height}")
224
++ logger.warning(f"end-height: {height}")
225
++ if node_profiler:
226
++ (root_path / "profile-node").rename("./profile-node")
tests/util/misc.py ADDED
@@@ -27,8 -27,8 +27,9 @@@ from chia.full_node.mempool import Memp
27
27
from chia.types.blockchain_format.sized_bytes import bytes32
28
28
from chia.types.condition_opcodes import ConditionOpcode
29
29
from chia.util.hash import std_hash
30
-- from chia.util.ints import uint64
30
++ from chia.util.ints import uint32, uint64
31
31
from chia.wallet.util.compute_hints import HintedCoin
32
++ from chia.wallet.wallet_node import WalletNode
32
33
from tests.core.data_layer.util import ChiaRoot
33
34

34
35

@@@ -424,3 -424,3 +425,8 @@@ def invariant_check_mempool(mempool: Me
424
425
if val is None:
425
426
val = 0
426
427
assert mempool._total_fee == val
428
++
429
++
430
++ async def wallet_height_at_least(wallet_node: WalletNode, h: uint32) -> bool:
431
++ height = await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()
432
++ return height == h
tests/util/run_block.py RENAMED
@@@ -1,0 -1,0 +1,161 @@@
1
++ from __future__ import annotations
2
++
3
++ import json
4
++ from dataclasses import dataclass
5
++ from pathlib import Path
6
++ from typing import Any, Dict, List, Tuple
7
++
8
++ from chia_rs import Coin
9
++
10
++ from chia.consensus.constants import ConsensusConstants
11
++ from chia.types.blockchain_format.serialized_program import SerializedProgram
12
++ from chia.types.blockchain_format.sized_bytes import bytes32
13
++ from chia.types.condition_opcodes import ConditionOpcode
14
++ from chia.types.condition_with_args import ConditionWithArgs
15
++ from chia.types.generator_types import BlockGenerator
16
++ from chia.util.ints import uint32, uint64
17
++ from chia.wallet.cat_wallet.cat_utils import match_cat_puzzle
18
++ from chia.wallet.puzzles.load_clvm import load_serialized_clvm_maybe_recompile
19
++ from chia.wallet.uncurried_puzzle import uncurry_puzzle
20
++
21
++ DESERIALIZE_MOD = load_serialized_clvm_maybe_recompile(
22
++ "chialisp_deserialisation.clsp", package_or_requirement="chia.consensus.puzzles"
23
++ )
24
++
25
++
26
++ @dataclass
27
++ class NPC:
28
++ coin_name: bytes32
29
++ puzzle_hash: bytes32
30
++ conditions: List[Tuple[ConditionOpcode, List[ConditionWithArgs]]]
31
++
32
++
33
++ @dataclass
34
++ class CAT:
35
++ asset_id: str
36
++ memo: str
37
++ npc: NPC
38
++
39
++ def cat_to_dict(self) -> Dict[str, Any]:
40
++ return {"asset_id": self.asset_id, "memo": self.memo, "npc": npc_to_dict(self.npc)}
41
++
42
++
43
++ def condition_with_args_to_dict(condition_with_args: ConditionWithArgs) -> Dict[str, Any]:
44
++ return {
45
++ "condition_opcode": condition_with_args.opcode.name,
46
++ "arguments": [arg.hex() for arg in condition_with_args.vars],
47
++ }
48
++
49
++
50
++ def condition_list_to_dict(condition_list: Tuple[ConditionOpcode, List[ConditionWithArgs]]) -> List[Dict[str, Any]]:
51
++ assert all([condition_list[0] == cwa.opcode for cwa in condition_list[1]])
52
++ return [condition_with_args_to_dict(cwa) for cwa in condition_list[1]]
53
++
54
++
55
++ def npc_to_dict(npc: NPC) -> Dict[str, Any]:
56
++ return {
57
++ "coin_name": npc.coin_name.hex(),
58
++ "conditions": [{"condition_type": c[0].name, "conditions": condition_list_to_dict(c)} for c in npc.conditions],
59
++ "puzzle_hash": npc.puzzle_hash.hex(),
60
++ }
61
++
62
++
63
++ def run_generator(block_generator: BlockGenerator, constants: ConsensusConstants, max_cost: int) -> List[CAT]:
64
++ block_args = [bytes(a) for a in block_generator.generator_refs]
65
++ cost, block_result = block_generator.program.run_with_cost(max_cost, [DESERIALIZE_MOD, block_args])
66
++
67
++ coin_spends = block_result.first()
68
++
69
++ cat_list: List[CAT] = []
70
++ for spend in coin_spends.as_iter():
71
++ parent, puzzle, amount, solution = spend.as_iter()
72
++ args = match_cat_puzzle(uncurry_puzzle(puzzle))
73
++
74
++ if args is None:
75
++ continue
76
++
77
++ _, asset_id, _ = args
78
++ memo = ""
79
++
80
++ puzzle_result = puzzle.run(solution)
81
++
82
++ conds: Dict[ConditionOpcode, List[ConditionWithArgs]] = {}
83
++
84
++ for condition in puzzle_result.as_python():
85
++ op = ConditionOpcode(condition[0])
86
++
87
++ if op not in conds:
88
++ conds[op] = []
89
++
90
++ if condition[0] != ConditionOpcode.CREATE_COIN or len(condition) < 4:
91
++ conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
92
++ continue
93
++
94
++ # If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount
95
++ if type(condition[3]) is not list:
96
++ # If it's not a list, it's not the correct format
97
++ conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
98
++ continue
99
++
100
++ conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]] + [condition[3][0]]))
101
++
102
++ # special retirement address
103
++ if condition[3][0].hex() != "0000000000000000000000000000000000000000000000000000000000000000":
104
++ continue
105
++
106
++ if len(condition[3]) >= 2:
107
++ try:
108
++ memo = condition[3][1].decode("utf-8", errors="strict")
109
++ except UnicodeError:
110
++ pass # ignore this error which should leave memo as empty string
111
++
112
++ # technically there could be more such create_coin ops in the list but our wallet does not
113
++ # so leaving it for the future
114
++ break
115
++
116
++ puzzle_hash = puzzle.get_tree_hash()
117
++ coin = Coin(bytes32(parent.as_atom()), puzzle_hash, amount.as_int())
118
++ cat_list.append(
119
++ CAT(
120
++ asset_id=bytes(asset_id).hex()[2:],
121
++ memo=memo,
122
++ npc=NPC(coin.name(), puzzle_hash, [(op, cond) for op, cond in conds.items()]),
123
++ )
124
++ )
125
++
126
++ return cat_list
127
++
128
++
129
++ def ref_list_to_args(ref_list: List[uint32], root_path: Path) -> List[SerializedProgram]:
130
++ args = []
131
++ for height in ref_list:
132
++ with open(root_path / f"{height}.json", "rb") as f:
133
++ program_str = json.load(f)["block"]["transactions_generator"]
134
++ args.append(SerializedProgram.fromhex(program_str))
135
++ return args
136
++
137
++
138
++ def run_generator_with_args(
139
++ generator_program_hex: str,
140
++ generator_args: List[SerializedProgram],
141
++ constants: ConsensusConstants,
142
++ cost: uint64,
143
++ ) -> List[CAT]:
144
++ if not generator_program_hex:
145
++ return []
146
++ generator_program = SerializedProgram.fromhex(generator_program_hex)
147
++ block_generator = BlockGenerator(generator_program, generator_args, [])
148
++ return run_generator(block_generator, constants, min(constants.MAX_BLOCK_COST_CLVM, cost))
149
++
150
++
151
++ def run_json_block(full_block: Dict[str, Any], parent: Path, constants: ConsensusConstants) -> List[CAT]:
152
++ ref_list = full_block["block"]["transactions_generator_ref_list"]
153
++ tx_info: Dict[str, Any] = full_block["block"]["transactions_info"]
154
++ generator_program_hex: str = full_block["block"]["transactions_generator"]
155
++ cat_list: List[CAT] = []
156
++ if tx_info and generator_program_hex:
157
++ cost = tx_info["cost"]
158
++ args = ref_list_to_args(ref_list, parent)
159
++ cat_list = run_generator_with_args(generator_program_hex, args, constants, cost)
160
++
161
++ return cat_list
tests/util/test_full_block_utils.py CHANGED
@@@ -6,7 -6,7 +6,6 @@@ from typing import Generator, Iterator
6
6
import pytest
7
7
from chia_rs import G1Element, G2Element
8
8

9
-- from benchmarks.utils import rand_bytes, rand_g1, rand_g2, rand_hash, rand_vdf, rand_vdf_proof, rewards
10
9
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
11
10
from chia.types.blockchain_format.pool_target import PoolTarget
12
11
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
@@@ -26,6 -26,6 +25,7 @@@ from chia.types.header_block import Hea
26
25
from chia.util.full_block_utils import block_info_from_block, generator_from_block, header_block_from_block
27
26
from chia.util.generator_tools import get_block_header
28
27
from chia.util.ints import uint8, uint32, uint64, uint128
28
++ from tests.util.benchmarks import rand_bytes, rand_g1, rand_g2, rand_hash, rand_vdf, rand_vdf_proof, rewards
29
29

30
30
test_g2s: List[G2Element] = [rand_g2() for _ in range(10)]
31
31
test_g1s: List[G1Element] = [rand_g1() for _ in range(10)]
tests/util/test_misc.py CHANGED
@@@ -1,14 -1,14 +1,16 @@@
1
1
from __future__ import annotations
2
2

3
3
import contextlib
4
-- from typing import AsyncIterator, Iterator, List
4
++ from typing import AsyncIterator, Iterator, List, Optional, TypeVar
5
5

6
++ import anyio
6
7
import pytest
7
8

8
9
from chia.util.errors import InvalidPathError
9
10
from chia.util.misc import (
10
11
SplitAsyncManager,
11
12
SplitManager,
13
++ ValuedEvent,
12
14
format_bytes,
13
15
format_minutes,
14
16
split_async_manager,
@@@ -16,6 -16,6 +18,9 @@@
16
18
to_batches,
17
19
validate_directory_writable,
18
20
)
21
++ from chia.util.timing import adjusted_timeout, backoff_times
22
++
23
++ T = TypeVar("T")
19
24

20
25

21
26
class TestMisc:
@@@ -306,3 -306,3 +311,103 @@@ async def test_split_async_manager_rais
306
311

307
312
with pytest.raises(Exception, match="^not yet entered$"):
308
313
await split.exit()
314
++
315
++
316
++ async def wait_for_valued_event_waiters(
317
++ event: ValuedEvent[T],
318
++ count: int,
319
++ timeout: float = 10,
320
++ ) -> None:
321
++ with anyio.fail_after(delay=adjusted_timeout(timeout)):
322
++ for delay in backoff_times():
323
++ # ignoring the type since i'm hacking into the private attribute
324
++ # hopefully this is ok for testing and if it becomes invalid we
325
++ # will end up with an exception and can adjust then
326
++ if len(event._event._waiters) >= count: # type: ignore[attr-defined]
327
++ return
328
++ await anyio.sleep(delay)
329
++
330
++
331
++ @pytest.mark.anyio
332
++ async def test_valued_event_wait_already_set() -> None:
333
++ valued_event = ValuedEvent[int]()
334
++ value = 37
335
++ valued_event.set(value)
336
++
337
++ with anyio.fail_after(adjusted_timeout(10)):
338
++ result = await valued_event.wait()
339
++
340
++ assert result == value
341
++
342
++
343
++ @pytest.mark.anyio
344
++ async def test_valued_event_wait_not_yet_set() -> None:
345
++ valued_event = ValuedEvent[int]()
346
++ value = 37
347
++ result: Optional[int] = None
348
++
349
++ async def wait(valued_event: ValuedEvent[int]) -> None:
350
++ nonlocal result
351
++ result = await valued_event.wait()
352
++
353
++ with anyio.fail_after(adjusted_timeout(10)):
354
++ async with anyio.create_task_group() as task_group:
355
++ task_group.start_soon(wait, valued_event)
356
++ await wait_for_valued_event_waiters(event=valued_event, count=1)
357
++ valued_event.set(value)
358
++
359
++ assert result == value
360
++
361
++
362
++ @pytest.mark.anyio
363
++ async def test_valued_event_wait_blocks_when_not_set() -> None:
364
++ valued_event = ValuedEvent[int]()
365
++ with pytest.raises(TimeoutError):
366
++ # if we could just process until there are no pending events, that would be great
367
++ with anyio.fail_after(adjusted_timeout(1)):
368
++ await valued_event.wait()
369
++
370
++
371
++ @pytest.mark.anyio
372
++ async def test_valued_event_multiple_waits_all_get_values() -> None:
373
++ results: List[int] = []
374
++ valued_event = ValuedEvent[int]()
375
++ value = 37
376
++ task_count = 10
377
++
378
++ async def wait_and_append() -> None:
379
++ results.append(await valued_event.wait())
380
++
381
++ async with anyio.create_task_group() as task_group:
382
++ for i in range(task_count):
383
++ task_group.start_soon(wait_and_append, name=f"wait_and_append_{i}")
384
++
385
++ await wait_for_valued_event_waiters(event=valued_event, count=task_count)
386
++ valued_event.set(value)
387
++
388
++ assert results == [value] * task_count
389
++
390
++
391
++ @pytest.mark.anyio
392
++ async def test_valued_event_set_again_raises_and_does_not_change_value() -> None:
393
++ valued_event = ValuedEvent[int]()
394
++ value = 37
395
++ valued_event.set(value)
396
++
397
++ with pytest.raises(Exception, match="^Value already set$"):
398
++ valued_event.set(value + 1)
399
++
400
++ with anyio.fail_after(adjusted_timeout(10)):
401
++ result = await valued_event.wait()
402
++
403
++ assert result == value
404
++
405
++
406
++ @pytest.mark.anyio
407
++ async def test_valued_event_wait_raises_if_not_set() -> None:
408
++ valued_event = ValuedEvent[int]()
409
++ valued_event._event.set()
410
++
411
++ with pytest.raises(Exception, match="^Value not set despite event being set$"):
412
++ with anyio.fail_after(adjusted_timeout(10)):
413
++ await valued_event.wait()
tests/util/test_network_protocol_test.py ADDED
@@@ -3,7 -3,7 +3,7 @@@ from __future__ import annotation
3
3

4
4
import ast
5
5
import inspect
6
-- from typing import Any, Set, cast
6
++ from typing import Any, Dict, Set, cast
7
7

8
8
from chia.protocols import (
9
9
farmer_protocol,
@@@ -11,6 -11,6 +11,7 @@@
11
11
harvester_protocol,
12
12
introducer_protocol,
13
13
pool_protocol,
14
++ protocol_message_types,
14
15
shared_protocol,
15
16
timelord_protocol,
16
17
wallet_protocol,
@@@ -47,6 -47,6 +48,30 @@@ def test_missing_messages_state_machine
47
48
), "A message was added to the protocol state machine. Make sure to update the protocol message regression test to include the new message"
48
49

49
50

51
++ def test_message_ids() -> None:
52
++ parsed = ast.parse(inspect.getsource(protocol_message_types))
53
++ message_ids: Dict[int, str] = {}
54
++ for line in parsed.body:
55
++ if not isinstance(line, ast.ClassDef) or line.name != "ProtocolMessageTypes":
56
++ continue
57
++ for entry in line.body:
58
++ if not isinstance(entry, ast.Assign): # pragma: no cover
59
++ continue
60
++ assert isinstance(entry.value, ast.Constant)
61
++ assert isinstance(entry.targets[0], ast.Name)
62
++ message_id = entry.value.value
63
++ message_name = entry.targets[0].id
64
++ if message_id in message_ids: # pragma: no cover
65
++ raise AssertionError(
66
++ f'protocol message ID clash between "{message_name}" and "{message_ids[message_id]}". Value {message_id}'
67
++ )
68
++ message_ids[message_id] = message_name
69
++ if message_id < 0 or message_id > 255: # pragma: no cover
70
++ raise AssertionError(f'message ID must fit in a uint8. "{message_name}" has value {message_id}')
71
++ break
72
++ assert len(message_ids) > 0
73
++
74
++
50
75
def test_missing_messages() -> None:
51
76
wallet_msgs = {
52
77
"CoinState",
tests/util/test_recursive_replace.py RENAMED
@@@ -1,0 -1,0 +1,116 @@@
1
++ from __future__ import annotations
2
++
3
++ import copy
4
++ from dataclasses import dataclass
5
++ from typing import List, Optional, Union
6
++
7
++ import pytest
8
++
9
++ from chia.util.recursive_replace import recursive_replace
10
++
11
++
12
++ class TestC:
13
++ a: int
14
++ b: str
15
++
16
++ def __init__(self, a: int, b: str):
17
++ self.a = a
18
++ self.b = b
19
++
20
++ # WARNING: this is just a simple stand in for rust classes and is not a good
21
++ # reference for how such a method should be implemented in python
22
++ def replace(self, **kwargs: Union[int, str, Optional[TestA]]) -> TestC:
23
++ ret = TestC(copy.deepcopy(self.a), copy.deepcopy(self.b))
24
++ for key, value in kwargs.items():
25
++ if key == "a":
26
++ ret.a = value # type: ignore[assignment]
27
++ elif key == "b": # pragma: no cover
28
++ ret.b = value # type: ignore[assignment]
29
++ else: # pragma: no cover
30
++ raise TypeError(f"unknown field {key}")
31
++ return ret
32
++
33
++
34
++ @dataclass
35
++ class TestA:
36
++ a: int
37
++ b: str
38
++ c: List[int]
39
++ d: Optional[TestC]
40
++
41
++
42
++ class TestB:
43
++ a: int
44
++ b: str
45
++ c: Optional[TestA]
46
++
47
++ def __init__(self, a: int, b: str, c: Optional[TestA]):
48
++ self.a = a
49
++ self.b = b
50
++ self.c = c
51
++
52
++ # WARNING: this is just a simple stand in for rust classes and is not a good
53
++ # reference for how such a method should be implemented in python
54
++ def replace(self, **kwargs: Union[int, str, Optional[TestA]]) -> TestB:
55
++ ret = TestB(copy.deepcopy(self.a), copy.deepcopy(self.b), copy.deepcopy(self.c))
56
++ for key, value in kwargs.items():
57
++ if key == "a": # pragma: no cover
58
++ ret.a = value # type: ignore[assignment]
59
++ elif key == "b":
60
++ ret.b = value # type: ignore[assignment]
61
++ elif key == "c":
62
++ ret.c = value # type: ignore[assignment]
63
++ else:
64
++ raise TypeError(f"unknown field {key}")
65
++ return ret
66
++
67
++ def __eq__(self, other: object) -> bool:
68
++ if isinstance(other, TestB):
69
++ return self.a == other.a and self.b == other.b and self.c == self.c
70
++ else:
71
++ return False # pragma: no cover
72
++
73
++
74
++ def test_recursive_replace_dataclass() -> None:
75
++ a = TestA(42, "foobar", [1337, 42], None)
76
++ a2 = recursive_replace(a, "b", "barfoo")
77
++
78
++ assert a.a == a2.a
79
++ assert a.b == "foobar"
80
++ assert a2.b == "barfoo"
81
++ assert a.c == a2.c
82
++
83
++
84
++ def test_recursive_replace_other() -> None:
85
++ b = TestB(42, "foobar", None)
86
++ b2 = recursive_replace(b, "b", "barfoo")
87
++
88
++ assert b.a == b2.a
89
++ assert b.b == "foobar"
90
++ assert b2.b == "barfoo"
91
++ assert b.c == b2.c
92
++
93
++
94
++ def test_recursive_replace() -> None:
95
++ b1 = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
96
++ b2 = recursive_replace(b1, "c.a", 110)
97
++
98
++ assert b1 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
99
++ assert b2 == TestB(42, "foobar", TestA(110, "barfoo", [1, 2, 3], None))
100
++
101
++
102
++ def test_recursive_replace2() -> None:
103
++ b1 = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(123, "345")))
104
++ b2 = recursive_replace(b1, "c.d.a", 110)
105
++
106
++ assert b1 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(123, "345")))
107
++ assert b2 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(110, "345")))
108
++
109
++
110
++ def test_recursive_replace_unknown() -> None:
111
++ b = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
112
++ with pytest.raises(TypeError):
113
++ recursive_replace(b, "c.foobar", 110)
114
++
115
++ with pytest.raises(TypeError):
116
++ recursive_replace(b, "foobar", 110)
tests/wallet/cat_wallet/test_cat_wallet.py CHANGED
@@@ -3,7 -3,7 +3,6 @@@ from __future__ import annotation
3
3
import asyncio
4
4
import tempfile
5
5
from pathlib import Path
6
-- from typing import List
7
6

8
7
import pytest
9
8

@@@ -11,7 -11,7 +10,6 @@@ from chia.consensus.block_rewards impor
11
10
from chia.protocols.wallet_protocol import CoinState
12
11
from chia.rpc.wallet_rpc_api import WalletRpcApi
13
12
from chia.rpc.wallet_rpc_client import WalletRpcClient
14
-- from chia.simulator.full_node_simulator import FullNodeSimulator
15
13
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
16
14
from chia.types.blockchain_format.coin import Coin, coin_as_list
17
15
from chia.types.blockchain_format.program import Program
@@@ -29,14 -29,14 +27,14 @@@ from chia.wallet.derivation_record impo
29
27
from chia.wallet.derive_keys import _derive_path_unhardened, master_sk_to_wallet_sk_unhardened_intermediate
30
28
from chia.wallet.lineage_proof import LineageProof
31
29
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_pk
32
-- from chia.wallet.transaction_record import TransactionRecord
33
30
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
34
31
from chia.wallet.util.wallet_types import WalletType
35
32
from chia.wallet.wallet_info import WalletInfo
36
33
from chia.wallet.wallet_interested_store import WalletInterestedStore
37
34
from chia.wallet.wallet_node import WalletNode
35
++ from chia.wallet.wallet_state_manager import WalletStateManager
38
36
from tests.conftest import ConsensusMode
39
-- from tests.util.setup_nodes import SimulatorsAndWalletsServices
37
++ from tests.util.setup_nodes import OldSimulatorsAndWallets, SimulatorsAndWalletsServices
40
38
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
41
39

42
40

@@@ -44,1016 -44,1016 +42,955 @@@ def check_wallets(node: WalletNode) ->
44
42
return len(node.wallet_state_manager.wallets.keys())
45
43

46
44

47
-- class TestCATWallet:
48
-- @pytest.mark.parametrize(
49
-- "trusted",
50
-- [True, False],
51
-- )
52
-- @pytest.mark.anyio
53
-- async def test_cat_creation(self, self_hostname, two_wallet_nodes, trusted):
54
-- num_blocks = 3
55
-- full_nodes, wallets, _ = two_wallet_nodes
56
-- full_node_api = full_nodes[0]
57
-- full_node_server = full_node_api.server
58
-- wallet_node, server_2 = wallets[0]
59
-- wallet = wallet_node.wallet_state_manager.main_wallet
60
--
61
-- ph = await wallet.get_new_puzzlehash()
62
-- if trusted:
63
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
64
-- else:
65
-- wallet_node.config["trusted_peers"] = {}
66
--
67
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
68
-- for i in range(0, num_blocks):
69
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
70
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
71
--
72
-- funds = sum(
73
-- [
74
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
75
-- for i in range(1, num_blocks + 1)
76
-- ]
77
-- )
78
--
79
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
80
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
81
--
82
-- async with wallet_node.wallet_state_manager.lock:
83
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
84
-- wallet_node.wallet_state_manager,
85
-- wallet,
86
-- {"identifier": "genesis_by_id"},
87
-- uint64(100),
88
-- DEFAULT_TX_CONFIG,
89
-- fee=uint64(10),
90
-- )
91
-- # The next 2 lines are basically a noop, it just adds test coverage
92
-- cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, cat_wallet.wallet_info)
93
-- await wallet_node.wallet_state_manager.add_new_wallet(cat_wallet)
94
--
95
-- tx_queue: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
96
-- tx_record = tx_queue[0]
97
-- await full_node_api.process_transaction_records(records=[tx_record])
98
--
99
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
100
-- await time_out_assert(20, cat_wallet.get_spendable_balance, 100)
101
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
102
-- await time_out_assert(20, wallet.get_confirmed_balance, funds - 110)
103
-- await time_out_assert(20, wallet.get_spendable_balance, funds - 110)
104
-- await time_out_assert(20, wallet.get_unconfirmed_balance, funds - 110)
105
--
106
-- # Test migration
107
-- all_lineage = await cat_wallet.lineage_store.get_all_lineage_proofs()
108
-- current_info = cat_wallet.wallet_info
109
-- data_str = bytes(
110
-- LegacyCATInfo(
111
-- cat_wallet.cat_info.limitations_program_hash, cat_wallet.cat_info.my_tail, list(all_lineage.items())
112
-- )
113
-- ).hex()
114
-- wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
115
-- new_cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, wallet_info)
116
-- assert new_cat_wallet.cat_info.limitations_program_hash == cat_wallet.cat_info.limitations_program_hash
117
-- assert new_cat_wallet.cat_info.my_tail == cat_wallet.cat_info.my_tail
118
-- assert await cat_wallet.lineage_store.get_all_lineage_proofs() == all_lineage
119
--
120
-- height = full_node_api.full_node.blockchain.get_peak_height()
121
-- await full_node_api.reorg_from_index_to_new_index(
122
-- ReorgProtocol(height - num_blocks - 1, height + 1, 32 * b"1", None)
123
-- )
124
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 0)
125
--
126
-- @pytest.mark.anyio
127
-- async def test_cat_creation_unique_lineage_store(self, self_hostname, two_wallet_nodes):
128
-- num_blocks = 3
129
-- full_nodes, wallets, _ = two_wallet_nodes
130
-- full_node_api = full_nodes[0]
131
-- full_node_server = full_node_api.server
132
-- wallet_node, wallet_server = wallets[0]
133
-- wallet = wallet_node.wallet_state_manager.main_wallet
134
-- ph = await wallet.get_new_puzzlehash()
45
++ @pytest.mark.parametrize("trusted", [True, False])
46
++ @pytest.mark.anyio
47
++ async def test_cat_creation(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
48
++ num_blocks = 3
49
++ full_nodes, wallets, _ = two_wallet_nodes
50
++ full_node_api = full_nodes[0]
51
++ full_node_server = full_node_api.server
52
++ wallet_node, server_2 = wallets[0]
53
++ wallet = wallet_node.wallet_state_manager.main_wallet
54
++
55
++ ph = await wallet.get_new_puzzlehash()
56
++ if trusted:
135
57
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
58
++ else:
59
++ wallet_node.config["trusted_peers"] = {}
136
60

137
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
138
-- for i in range(0, num_blocks):
139
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
140
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
61
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
62
++ for _ in range(num_blocks):
63
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
64
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
141
65

142
-- funds = sum(
143
-- [
144
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
145
-- for i in range(1, num_blocks + 1)
146
-- ]
147
-- )
66
++ funds = sum(
67
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
68
++ )
69
++
70
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
71
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
148
72

149
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
150
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
151
--
152
-- async with wallet_node.wallet_state_manager.lock:
153
-- cat_wallet_1, _ = await CATWallet.create_new_cat_wallet(
154
-- wallet_node.wallet_state_manager,
155
-- wallet,
156
-- {"identifier": "genesis_by_id"},
157
-- uint64(100),
158
-- DEFAULT_TX_CONFIG,
159
-- )
160
-- cat_wallet_2, _ = await CATWallet.create_new_cat_wallet(
161
-- wallet_node.wallet_state_manager,
162
-- wallet,
163
-- {"identifier": "genesis_by_id"},
164
-- uint64(200),
165
-- DEFAULT_TX_CONFIG,
166
-- )
167
--
168
-- proofs_1 = await cat_wallet_1.lineage_store.get_all_lineage_proofs()
169
-- proofs_2 = await cat_wallet_2.lineage_store.get_all_lineage_proofs()
170
-- assert len(proofs_1) == len(proofs_2)
171
-- assert proofs_1 != proofs_2
172
-- assert cat_wallet_1.lineage_store.table_name != cat_wallet_2.lineage_store.table_name
173
--
174
-- @pytest.mark.parametrize(
175
-- "trusted",
176
-- [True, False],
73
++ async with wallet_node.wallet_state_manager.lock:
74
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
75
++ wallet_node.wallet_state_manager,
76
++ wallet,
77
++ {"identifier": "genesis_by_id"},
78
++ uint64(100),
79
++ DEFAULT_TX_CONFIG,
80
++ fee=uint64(10),
81
++ )
82
++ # The next 2 lines are basically a noop, it just adds test coverage
83
++ cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, cat_wallet.wallet_info)
84
++ await wallet_node.wallet_state_manager.add_new_wallet(cat_wallet)
85
++
86
++ tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
87
++ tx_record = tx_queue[0]
88
++ await full_node_api.process_transaction_records(records=[tx_record])
89
++
90
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
91
++ await time_out_assert(20, cat_wallet.get_spendable_balance, 100)
92
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
93
++ await time_out_assert(20, wallet.get_confirmed_balance, funds - 110)
94
++ await time_out_assert(20, wallet.get_spendable_balance, funds - 110)
95
++ await time_out_assert(20, wallet.get_unconfirmed_balance, funds - 110)
96
++
97
++ # Test migration
98
++ all_lineage = await cat_wallet.lineage_store.get_all_lineage_proofs()
99
++ current_info = cat_wallet.wallet_info
100
++ data_str = bytes(
101
++ LegacyCATInfo(
102
++ cat_wallet.cat_info.limitations_program_hash, cat_wallet.cat_info.my_tail, list(all_lineage.items())
103
++ )
104
++ ).hex()
105
++ wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
106
++ new_cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, wallet_info)
107
++ assert new_cat_wallet.cat_info.limitations_program_hash == cat_wallet.cat_info.limitations_program_hash
108
++ assert new_cat_wallet.cat_info.my_tail == cat_wallet.cat_info.my_tail
109
++ assert await cat_wallet.lineage_store.get_all_lineage_proofs() == all_lineage
110
++
111
++ height = full_node_api.full_node.blockchain.get_peak_height()
112
++ assert height is not None
113
++ await full_node_api.reorg_from_index_to_new_index(
114
++ ReorgProtocol(uint32(height - num_blocks - 1), uint32(height + 1), bytes32(32 * b"1"), None)
177
115
)
178
-- @pytest.mark.anyio
179
-- async def test_cat_spend(self, self_hostname, two_wallet_nodes, trusted):
180
-- num_blocks = 3
181
-- full_nodes, wallets, _ = two_wallet_nodes
182
-- full_node_api = full_nodes[0]
183
-- full_node_server = full_node_api.server
184
-- wallet_node, server_2 = wallets[0]
185
-- wallet_node_2, server_3 = wallets[1]
186
-- wallet = wallet_node.wallet_state_manager.main_wallet
187
-- wallet2 = wallet_node_2.wallet_state_manager.main_wallet
188
-- api_0 = WalletRpcApi(wallet_node)
189
-- api_1 = WalletRpcApi(wallet_node_2)
190
-- ph = await wallet.get_new_puzzlehash()
191
-- if trusted:
192
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
193
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
194
-- else:
195
-- wallet_node.config["trusted_peers"] = {}
196
-- wallet_node_2.config["trusted_peers"] = {}
197
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
198
-- await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
199
--
200
-- for i in range(0, num_blocks):
201
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
202
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
203
--
204
-- funds = sum(
205
-- [
206
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
207
-- for i in range(1, num_blocks + 1)
208
-- ]
209
-- )
116
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 0)
210
117

211
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
212
118

213
-- async with wallet_node.wallet_state_manager.lock:
214
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
215
-- wallet_node.wallet_state_manager,
216
-- wallet,
217
-- {"identifier": "genesis_by_id"},
218
-- uint64(100),
219
-- DEFAULT_TX_CONFIG,
220
-- )
221
-- tx_queue: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
222
-- tx_record = tx_queue[0]
223
-- await full_node_api.process_transaction_records(records=[tx_record])
119
++ @pytest.mark.anyio
120
++ async def test_cat_creation_unique_lineage_store(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets) -> None:
121
++ num_blocks = 3
122
++ full_nodes, wallets, _ = two_wallet_nodes
123
++ full_node_api = full_nodes[0]
124
++ full_node_server = full_node_api.server
125
++ wallet_node, wallet_server = wallets[0]
126
++ wallet = wallet_node.wallet_state_manager.main_wallet
127
++ ph = await wallet.get_new_puzzlehash()
128
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
129
++
130
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
131
++ for _ in range(num_blocks):
132
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
133
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
224
134

225
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
226
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
135
++ funds = sum(
136
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
137
++ )
227
138

228
-- assert cat_wallet.cat_info.limitations_program_hash is not None
229
-- asset_id = cat_wallet.get_asset_id()
139
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
140
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
230
141

231
-- cat_wallet_2: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
232
-- wallet_node_2.wallet_state_manager, wallet2, asset_id
142
++ async with wallet_node.wallet_state_manager.lock:
143
++ cat_wallet_1, _ = await CATWallet.create_new_cat_wallet(
144
++ wallet_node.wallet_state_manager,
145
++ wallet,
146
++ {"identifier": "genesis_by_id"},
147
++ uint64(100),
148
++ DEFAULT_TX_CONFIG,
149
++ )
150
++ cat_wallet_2, _ = await CATWallet.create_new_cat_wallet(
151
++ wallet_node.wallet_state_manager,
152
++ wallet,
153
++ {"identifier": "genesis_by_id"},
154
++ uint64(200),
155
++ DEFAULT_TX_CONFIG,
233
156
)
234
157

235
-- assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
158
++ proofs_1 = await cat_wallet_1.lineage_store.get_all_lineage_proofs()
159
++ proofs_2 = await cat_wallet_2.lineage_store.get_all_lineage_proofs()
160
++ assert len(proofs_1) == len(proofs_2)
161
++ assert proofs_1 != proofs_2
162
++ assert cat_wallet_1.lineage_store.table_name != cat_wallet_2.lineage_store.table_name
236
163

237
-- cat_2_hash = await cat_wallet_2.get_new_inner_hash()
238
-- tx_records = await cat_wallet.generate_signed_transaction(
239
-- [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
240
-- )
241
-- tx_id = None
242
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
243
-- for tx_record in tx_records:
244
-- if tx_record.wallet_id is cat_wallet.id():
245
-- tx_id = tx_record.name.hex()
246
-- assert tx_record.to_puzzle_hash == cat_2_hash
247
--
248
-- await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
249
--
250
-- await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
251
-- memos = await api_0.get_transaction_memo(dict(transaction_id=tx_id))
252
-- assert len(memos[tx_id]) == 2 # One for tx, one for change
253
-- assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
254
--
255
-- for i in range(1, num_blocks):
256
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
257
--
258
-- await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
259
--
260
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
261
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
262
--
263
-- await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
264
-- await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
265
-- coins = await cat_wallet_2.select_coins(uint64(60), DEFAULT_COIN_SELECTION_CONFIG)
266
-- assert len(coins) == 1
267
-- coin = coins.pop()
268
-- tx_id = coin.name().hex()
269
-- memos = await api_1.get_transaction_memo(dict(transaction_id=tx_id))
270
-- assert len(memos[tx_id]) == 2
271
-- assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
272
-- cat_hash = await cat_wallet.get_new_inner_hash()
273
-- tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
274
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
275
--
276
-- await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
277
164

165
++ @pytest.mark.parametrize("trusted", [True, False])
166
++ @pytest.mark.anyio
167
++ async def test_cat_spend(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
168
++ num_blocks = 3
169
++ full_nodes, wallets, _ = two_wallet_nodes
170
++ full_node_api = full_nodes[0]
171
++ full_node_server = full_node_api.server
172
++ wallet_node, server_2 = wallets[0]
173
++ wallet_node_2, server_3 = wallets[1]
174
++ wallet = wallet_node.wallet_state_manager.main_wallet
175
++ wallet2 = wallet_node_2.wallet_state_manager.main_wallet
176
++ api_0 = WalletRpcApi(wallet_node)
177
++ api_1 = WalletRpcApi(wallet_node_2)
178
++ ph = await wallet.get_new_puzzlehash()
179
++ if trusted:
180
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
181
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
182
++ else:
183
++ wallet_node.config["trusted_peers"] = {}
184
++ wallet_node_2.config["trusted_peers"] = {}
185
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
186
++ await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
187
++
188
++ for _ in range(num_blocks):
278
189
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
190
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
279
191

280
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
281
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
192
++ funds = sum(
193
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
194
++ )
282
195

283
-- height = full_node_api.full_node.blockchain.get_peak_height()
284
-- await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - 1, height + 1, 32 * b"1", None))
285
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
196
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
286
197

287
-- @pytest.mark.parametrize(
288
-- "trusted",
289
-- [True, False],
290
-- )
291
-- @pytest.mark.anyio
292
-- async def test_cat_reuse_address(self, self_hostname, two_wallet_nodes, trusted):
293
-- num_blocks = 3
294
-- full_nodes, wallets, _ = two_wallet_nodes
295
-- full_node_api = full_nodes[0]
296
-- full_node_server = full_node_api.server
297
-- wallet_node, server_2 = wallets[0]
298
-- wallet_node_2, server_3 = wallets[1]
299
-- wallet = wallet_node.wallet_state_manager.main_wallet
300
-- wallet2 = wallet_node_2.wallet_state_manager.main_wallet
301
--
302
-- ph = await wallet.get_new_puzzlehash()
303
-- if trusted:
304
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
305
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
306
-- else:
307
-- wallet_node.config["trusted_peers"] = {}
308
-- wallet_node_2.config["trusted_peers"] = {}
309
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
310
-- await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
311
--
312
-- for i in range(0, num_blocks):
313
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
314
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
315
--
316
-- funds = sum(
317
-- [
318
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
319
-- for i in range(1, num_blocks + 1)
320
-- ]
198
++ async with wallet_node.wallet_state_manager.lock:
199
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
200
++ wallet_node.wallet_state_manager,
201
++ wallet,
202
++ {"identifier": "genesis_by_id"},
203
++ uint64(100),
204
++ DEFAULT_TX_CONFIG,
321
205
)
206
++ tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
207
++ tx_record = tx_queue[0]
208
++ await full_node_api.process_transaction_records(records=[tx_record])
322
209

323
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
210
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
211
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
324
212

325
-- async with wallet_node.wallet_state_manager.lock:
326
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
327
-- wallet_node.wallet_state_manager,
328
-- wallet,
329
-- {"identifier": "genesis_by_id"},
330
-- uint64(100),
331
-- DEFAULT_TX_CONFIG,
332
-- )
333
-- tx_queue: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
334
-- tx_record = tx_queue[0]
335
-- await full_node_api.process_transaction_records(records=[tx_record])
213
++ assert cat_wallet.cat_info.limitations_program_hash is not None
214
++ asset_id = cat_wallet.get_asset_id()
336
215

337
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
338
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
216
++ cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
339
217

340
-- assert cat_wallet.cat_info.limitations_program_hash is not None
341
-- asset_id = cat_wallet.get_asset_id()
218
++ assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
342
219

343
-- cat_wallet_2: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
344
-- wallet_node_2.wallet_state_manager, wallet2, asset_id
345
-- )
220
++ cat_2_hash = await cat_wallet_2.get_new_inner_hash()
221
++ tx_records = await cat_wallet.generate_signed_transaction(
222
++ [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
223
++ )
224
++ tx_id = None
225
++ tx_records = await wallet.wallet_state_manager.add_pending_transactions(tx_records)
226
++ for tx_record in tx_records:
227
++ if tx_record.wallet_id is cat_wallet.id():
228
++ tx_id = tx_record.name.hex()
229
++ assert tx_record.to_puzzle_hash == cat_2_hash
230
++
231
++ await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
232
++
233
++ await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
234
++ assert tx_id is not None
235
++ memos = await api_0.get_transaction_memo({"transaction_id": tx_id})
236
++ assert len(memos[tx_id]) == 2 # One for tx, one for change
237
++ assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
238
++
239
++ for _ in range(1, num_blocks):
240
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"\0")))
241
++
242
++ await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
243
++
244
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
245
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
246
++
247
++ await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
248
++ await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
249
++ coins = await cat_wallet_2.select_coins(uint64(60), DEFAULT_COIN_SELECTION_CONFIG)
250
++ assert len(coins) == 1
251
++ coin = coins.pop()
252
++ tx_id = coin.name().hex()
253
++ memos = await api_1.get_transaction_memo(dict(transaction_id=tx_id))
254
++ assert len(memos[tx_id]) == 2
255
++ assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
256
++ cat_hash = await cat_wallet.get_new_inner_hash()
257
++ tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
258
++ tx_records = await wallet.wallet_state_manager.add_pending_transactions(tx_records)
259
++
260
++ await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
261
++
262
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
263
++
264
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
265
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
266
++
267
++ height = full_node_api.full_node.blockchain.get_peak_height()
268
++ assert height is not None
269
++ await full_node_api.reorg_from_index_to_new_index(
270
++ ReorgProtocol(uint32(height - 1), uint32(height + 1), bytes32(32 * b"1"), None)
271
++ )
272
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
273
++
274
++
275
++ @pytest.mark.parametrize("trusted", [True, False])
276
++ @pytest.mark.anyio
277
++ async def test_cat_reuse_address(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
278
++ num_blocks = 3
279
++ full_nodes, wallets, _ = two_wallet_nodes
280
++ full_node_api = full_nodes[0]
281
++ full_node_server = full_node_api.server
282
++ wallet_node, server_2 = wallets[0]
283
++ wallet_node_2, server_3 = wallets[1]
284
++ wallet = wallet_node.wallet_state_manager.main_wallet
285
++ wallet2 = wallet_node_2.wallet_state_manager.main_wallet
286
++
287
++ ph = await wallet.get_new_puzzlehash()
288
++ if trusted:
289
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
290
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
291
++ else:
292
++ wallet_node.config["trusted_peers"] = {}
293
++ wallet_node_2.config["trusted_peers"] = {}
294
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
295
++ await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
296
++
297
++ for _ in range(num_blocks):
298
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
299
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
300
++
301
++ funds = sum(
302
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
303
++ )
346
304

347
-- assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
305
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
348
306

349
-- cat_2_hash = await cat_wallet_2.get_new_inner_hash()
350
-- tx_records = await cat_wallet.generate_signed_transaction(
351
-- [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG.override(reuse_puzhash=True), fee=uint64(1)
307
++ async with wallet_node.wallet_state_manager.lock:
308
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
309
++ wallet_node.wallet_state_manager,
310
++ wallet,
311
++ {"identifier": "genesis_by_id"},
312
++ uint64(100),
313
++ DEFAULT_TX_CONFIG,
352
314
)
353
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
354
-- for tx_record in tx_records:
355
-- if tx_record.wallet_id is cat_wallet.id():
356
-- assert tx_record.to_puzzle_hash == cat_2_hash
357
-- assert len(tx_record.spend_bundle.coin_spends) == 2
358
-- for cs in tx_record.spend_bundle.coin_spends:
359
-- if cs.coin.amount == 100:
360
-- old_puzhash = cs.coin.puzzle_hash.hex()
361
-- new_puzhash = [c.puzzle_hash.hex() for c in tx_record.additions]
362
-- assert old_puzhash in new_puzhash
315
++ tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
316
++ tx_record = tx_queue[0]
317
++ await full_node_api.process_transaction_records(records=[tx_record])
363
318

364
-- await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
319
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
320
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
365
321

366
-- await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
322
++ assert cat_wallet.cat_info.limitations_program_hash is not None
323
++ asset_id = cat_wallet.get_asset_id()
367
324

368
-- for i in range(1, num_blocks):
369
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
325
++ cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
370
326

371
-- await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
327
++ assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
372
328

373
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
374
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
329
++ cat_2_hash = await cat_wallet_2.get_new_inner_hash()
330
++ tx_records = await cat_wallet.generate_signed_transaction(
331
++ [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG.override(reuse_puzhash=True), fee=uint64(1)
332
++ )
333
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
334
++ for tx_record in tx_records:
335
++ if tx_record.wallet_id is cat_wallet.id():
336
++ assert tx_record.to_puzzle_hash == cat_2_hash
337
++ assert tx_record.spend_bundle is not None
338
++ assert len(tx_record.spend_bundle.coin_spends) == 2
339
++ for cs in tx_record.spend_bundle.coin_spends:
340
++ if cs.coin.amount == 100:
341
++ old_puzhash = cs.coin.puzzle_hash.hex()
342
++ new_puzhash = [c.puzzle_hash.hex() for c in tx_record.additions]
343
++ assert old_puzhash in new_puzhash
375
344

376
-- await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
377
-- await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
345
++ await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
378
346

379
-- cat_hash = await cat_wallet.get_new_inner_hash()
380
-- tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
381
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
347
++ await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
382
348

383
-- await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
349
++ for _ in range(1, num_blocks):
350
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"\0")))
384
351

385
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
352
++ await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
353
++
354
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
355
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
356
++
357
++ await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
358
++ await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
386
359

387
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
388
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
360
++ cat_hash = await cat_wallet.get_new_inner_hash()
361
++ tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
362
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
389
363

390
-- height = full_node_api.full_node.blockchain.get_peak_height()
391
-- await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - 1, height + 1, 32 * b"1", None))
392
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
364
++ await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
393
365

394
-- @pytest.mark.parametrize(
395
-- "trusted",
396
-- [True, False],
366
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
367
++
368
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
369
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
370
++
371
++ height = full_node_api.full_node.blockchain.get_peak_height()
372
++ assert height is not None
373
++ await full_node_api.reorg_from_index_to_new_index(
374
++ ReorgProtocol(uint32(height - 1), uint32(height + 1), bytes32(32 * b"1"), None)
397
375
)
398
-- @pytest.mark.anyio
399
-- async def test_get_wallet_for_asset_id(self, self_hostname, two_wallet_nodes, trusted):
400
-- num_blocks = 3
401
-- full_nodes, wallets, _ = two_wallet_nodes
402
-- full_node_api = full_nodes[0]
403
-- full_node_server = full_node_api.server
404
-- wallet_node, server_2 = wallets[0]
405
-- wallet = wallet_node.wallet_state_manager.main_wallet
406
--
407
-- ph = await wallet.get_new_puzzlehash()
408
-- if trusted:
409
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
410
-- else:
411
-- wallet_node.config["trusted_peers"] = {}
412
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
413
--
414
-- for i in range(0, num_blocks):
415
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
416
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
417
--
418
-- funds = sum(
419
-- [
420
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
421
-- for i in range(1, num_blocks + 1)
422
-- ]
423
-- )
376
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
377
++
424
378

425
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
426
--
427
-- async with wallet_node.wallet_state_manager.lock:
428
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
429
-- wallet_node.wallet_state_manager,
430
-- wallet,
431
-- {"identifier": "genesis_by_id"},
432
-- uint64(100),
433
-- DEFAULT_TX_CONFIG,
434
-- )
435
--
436
-- for i in range(1, num_blocks):
437
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
438
--
439
-- asset_id = cat_wallet.get_asset_id()
440
-- await cat_wallet.set_tail_program(bytes(cat_wallet.cat_info.my_tail).hex())
441
-- assert await wallet_node.wallet_state_manager.get_wallet_for_asset_id(asset_id) == cat_wallet
442
--
443
-- # Test that the a default CAT will initialize correctly
444
-- asset = DEFAULT_CATS[next(iter(DEFAULT_CATS))]
445
-- asset_id = asset["asset_id"]
446
-- cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node.wallet_state_manager, wallet, asset_id)
447
-- assert cat_wallet_2.get_name() == asset["name"]
448
-- await cat_wallet_2.set_name("Test Name")
449
-- assert cat_wallet_2.get_name() == "Test Name"
450
--
451
-- @pytest.mark.parametrize(
452
-- "trusted",
453
-- [True, False],
379
++ @pytest.mark.parametrize("trusted", [True, False])
380
++ @pytest.mark.anyio
381
++ async def test_get_wallet_for_asset_id(
382
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
383
++ ) -> None:
384
++ num_blocks = 3
385
++ full_nodes, wallets, _ = two_wallet_nodes
386
++ full_node_api = full_nodes[0]
387
++ full_node_server = full_node_api.server
388
++ wallet_node, server_2 = wallets[0]
389
++ wallet = wallet_node.wallet_state_manager.main_wallet
390
++
391
++ ph = await wallet.get_new_puzzlehash()
392
++ if trusted:
393
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
394
++ else:
395
++ wallet_node.config["trusted_peers"] = {}
396
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
397
++
398
++ for _ in range(num_blocks):
399
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
400
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
401
++
402
++ funds = sum(
403
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
454
404
)
455
-- @pytest.mark.anyio
456
-- async def test_cat_doesnt_see_eve(self, self_hostname, two_wallet_nodes, trusted):
457
-- num_blocks = 3
458
-- full_nodes, wallets, _ = two_wallet_nodes
459
-- full_node_api = full_nodes[0]
460
-- full_node_server = full_node_api.server
461
-- wallet_node, server_2 = wallets[0]
462
-- wallet_node_2, server_3 = wallets[1]
463
-- wallet = wallet_node.wallet_state_manager.main_wallet
464
-- wallet2 = wallet_node_2.wallet_state_manager.main_wallet
465
--
466
-- ph = await wallet.get_new_puzzlehash()
467
-- if trusted:
468
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
469
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
470
-- else:
471
-- wallet_node.config["trusted_peers"] = {}
472
-- wallet_node_2.config["trusted_peers"] = {}
473
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
474
-- await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
475
--
476
-- for i in range(0, num_blocks):
477
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
478
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
479
--
480
-- funds = sum(
481
-- [
482
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
483
-- for i in range(1, num_blocks + 1)
484
-- ]
405
++
406
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
407
++
408
++ async with wallet_node.wallet_state_manager.lock:
409
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
410
++ wallet_node.wallet_state_manager,
411
++ wallet,
412
++ {"identifier": "genesis_by_id"},
413
++ uint64(100),
414
++ DEFAULT_TX_CONFIG,
485
415
)
486
416

487
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
417
++ for _ in range(1, num_blocks):
418
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
488
419

489
-- async with wallet_node.wallet_state_manager.lock:
490
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
491
-- wallet_node.wallet_state_manager,
492
-- wallet,
493
-- {"identifier": "genesis_by_id"},
494
-- uint64(100),
495
-- DEFAULT_TX_CONFIG,
496
-- )
497
-- tx_records: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
498
-- await full_node_api.process_transaction_records(records=tx_records)
420
++ asset_id = cat_wallet.get_asset_id()
421
++ assert cat_wallet.cat_info.my_tail is not None
422
++ await cat_wallet.set_tail_program(bytes(cat_wallet.cat_info.my_tail).hex())
423
++ assert await wallet_node.wallet_state_manager.get_wallet_for_asset_id(asset_id) == cat_wallet
499
424

500
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
501
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
425
++ # Test that the a default CAT will initialize correctly
426
++ asset = DEFAULT_CATS[next(iter(DEFAULT_CATS))]
427
++ asset_id = asset["asset_id"]
428
++ cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node.wallet_state_manager, wallet, asset_id)
429
++ assert cat_wallet_2.get_name() == asset["name"]
430
++ await cat_wallet_2.set_name("Test Name")
431
++ assert cat_wallet_2.get_name() == "Test Name"
502
432

503
-- assert cat_wallet.cat_info.limitations_program_hash is not None
504
-- asset_id = cat_wallet.get_asset_id()
505
433

506
-- cat_wallet_2: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
507
-- wallet_node_2.wallet_state_manager, wallet2, asset_id
508
-- )
434
++ @pytest.mark.parametrize("trusted", [True, False])
435
++ @pytest.mark.anyio
436
++ async def test_cat_doesnt_see_eve(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
437
++ num_blocks = 3
438
++ full_nodes, wallets, _ = two_wallet_nodes
439
++ full_node_api = full_nodes[0]
440
++ full_node_server = full_node_api.server
441
++ wallet_node, server_2 = wallets[0]
442
++ wallet_node_2, server_3 = wallets[1]
443
++ wallet = wallet_node.wallet_state_manager.main_wallet
444
++ wallet2 = wallet_node_2.wallet_state_manager.main_wallet
445
++
446
++ ph = await wallet.get_new_puzzlehash()
447
++ if trusted:
448
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
449
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
450
++ else:
451
++ wallet_node.config["trusted_peers"] = {}
452
++ wallet_node_2.config["trusted_peers"] = {}
453
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
454
++ await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
455
++
456
++ for _ in range(num_blocks):
457
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
458
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
509
459

510
-- assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
460
++ funds = sum(
461
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
462
++ )
463
++
464
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
511
465

512
-- cat_2_hash = await cat_wallet_2.get_new_inner_hash()
513
-- tx_records = await cat_wallet.generate_signed_transaction(
514
-- [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
466
++ async with wallet_node.wallet_state_manager.lock:
467
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
468
++ wallet_node.wallet_state_manager,
469
++ wallet,
470
++ {"identifier": "genesis_by_id"},
471
++ uint64(100),
472
++ DEFAULT_TX_CONFIG,
515
473
)
516
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
517
-- await full_node_api.process_transaction_records(records=tx_records)
474
++ tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
475
++ await full_node_api.process_transaction_records(records=tx_records)
518
476

519
-- await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
520
-- await time_out_assert(30, wallet.get_unconfirmed_balance, funds - 101)
477
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
478
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
521
479

522
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
523
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
480
++ assert cat_wallet.cat_info.limitations_program_hash is not None
481
++ asset_id = cat_wallet.get_asset_id()
524
482

525
-- await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
526
-- await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
483
++ cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
527
484

528
-- cc2_ph = await cat_wallet_2.get_new_cat_puzzle_hash()
529
-- [tx_record] = await wallet.wallet_state_manager.main_wallet.generate_signed_transaction(
530
-- 10, cc2_ph, DEFAULT_TX_CONFIG, 0
531
-- )
532
-- await wallet.wallet_state_manager.add_pending_transactions([tx_record])
533
-- await full_node_api.process_transaction_records(records=[tx_record])
485
++ assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
534
486

535
-- id = cat_wallet_2.id()
536
-- wsm = cat_wallet_2.wallet_state_manager
487
++ cat_2_hash = await cat_wallet_2.get_new_inner_hash()
488
++ tx_records = await cat_wallet.generate_signed_transaction(
489
++ [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
490
++ )
491
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
492
++ await full_node_api.process_transaction_records(records=tx_records)
493
++
494
++ await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
495
++ await time_out_assert(30, wallet.get_unconfirmed_balance, funds - 101)
537
496

538
-- async def query_and_assert_transactions(wsm, id):
539
-- all_txs = await wsm.tx_store.get_all_transactions_for_wallet(id)
540
-- return len(list(filter(lambda tx: tx.amount == 10, all_txs)))
497
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
498
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
541
499

542
-- await time_out_assert(20, query_and_assert_transactions, 0, wsm, id)
543
-- await time_out_assert(20, wsm.get_confirmed_balance_for_wallet, 60, id)
544
-- await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
545
-- await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
500
++ await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
501
++ await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
546
502

547
-- @pytest.mark.parametrize(
548
-- "trusted",
549
-- [True, False],
503
++ cc2_ph = await cat_wallet_2.get_new_cat_puzzle_hash()
504
++ [tx_record] = await wallet.wallet_state_manager.main_wallet.generate_signed_transaction(
505
++ uint64(10), cc2_ph, DEFAULT_TX_CONFIG
550
506
)
551
-- @pytest.mark.anyio
552
-- async def test_cat_spend_multiple(self, self_hostname, three_wallet_nodes, trusted):
553
-- num_blocks = 3
554
-- full_nodes, wallets, _ = three_wallet_nodes
555
-- full_node_api = full_nodes[0]
556
-- full_node_server = full_node_api.server
557
-- wallet_node_0, wallet_server_0 = wallets[0]
558
-- wallet_node_1, wallet_server_1 = wallets[1]
559
-- wallet_node_2, wallet_server_2 = wallets[2]
560
-- wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
561
-- wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
562
-- wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
563
--
564
-- ph = await wallet_0.get_new_puzzlehash()
565
-- if trusted:
566
-- wallet_node_0.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
567
-- wallet_node_1.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
568
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
569
-- else:
570
-- wallet_node_0.config["trusted_peers"] = {}
571
-- wallet_node_1.config["trusted_peers"] = {}
572
-- wallet_node_2.config["trusted_peers"] = {}
573
-- await wallet_server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
574
-- await wallet_server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
575
-- await wallet_server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
576
--
577
-- for i in range(0, num_blocks):
578
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
579
--
580
-- funds = sum(
581
-- [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
582
-- )
507
++ await wallet.wallet_state_manager.add_pending_transactions([tx_record])
508
++ await full_node_api.process_transaction_records(records=[tx_record])
583
509

584
-- await time_out_assert(20, wallet_0.get_confirmed_balance, funds)
510
++ id = cat_wallet_2.id()
511
++ wsm = cat_wallet_2.wallet_state_manager
585
512

586
-- async with wallet_node_0.wallet_state_manager.lock:
587
-- cat_wallet_0, _ = await CATWallet.create_new_cat_wallet(
588
-- wallet_node_0.wallet_state_manager,
589
-- wallet_0,
590
-- {"identifier": "genesis_by_id"},
591
-- uint64(100),
592
-- DEFAULT_TX_CONFIG,
593
-- )
594
-- tx_records: List[TransactionRecord] = await wallet_node_0.wallet_state_manager.tx_store.get_not_sent()
595
-- await full_node_api.process_transaction_records(records=tx_records)
513
++ async def query_and_assert_transactions(wsm: WalletStateManager, id: uint32) -> int:
514
++ all_txs = await wsm.tx_store.get_all_transactions_for_wallet(id)
515
++ return len(list(filter(lambda tx: tx.amount == 10, all_txs)))
596
516

597
-- await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 100)
598
-- await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 100)
517
++ await time_out_assert(20, query_and_assert_transactions, 0, wsm, id)
518
++ await time_out_assert(20, wsm.get_confirmed_balance_for_wallet, 60, id)
519
++ await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
520
++ await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
599
521

600
-- assert cat_wallet_0.cat_info.limitations_program_hash is not None
601
-- asset_id = cat_wallet_0.get_asset_id()
602
522

603
-- cat_wallet_1: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
604
-- wallet_node_1.wallet_state_manager, wallet_1, asset_id
605
-- )
523
++ @pytest.mark.parametrize("trusted", [True, False])
524
++ @pytest.mark.anyio
525
++ async def test_cat_spend_multiple(
526
++ self_hostname: str, three_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
527
++ ) -> None:
528
++ num_blocks = 3
529
++ full_nodes, wallets, _ = three_wallet_nodes
530
++ full_node_api = full_nodes[0]
531
++ full_node_server = full_node_api.server
532
++ wallet_node_0, wallet_server_0 = wallets[0]
533
++ wallet_node_1, wallet_server_1 = wallets[1]
534
++ wallet_node_2, wallet_server_2 = wallets[2]
535
++ wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
536
++ wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
537
++ wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
538
++
539
++ ph = await wallet_0.get_new_puzzlehash()
540
++ if trusted:
541
++ wallet_node_0.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
542
++ wallet_node_1.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
543
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
544
++ else:
545
++ wallet_node_0.config["trusted_peers"] = {}
546
++ wallet_node_1.config["trusted_peers"] = {}
547
++ wallet_node_2.config["trusted_peers"] = {}
548
++ await wallet_server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
549
++ await wallet_server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
550
++ await wallet_server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
551
++
552
++ for _ in range(num_blocks):
553
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
554
++
555
++ funds = sum(
556
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
557
++ )
606
558

607
-- cat_wallet_2: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
608
-- wallet_node_2.wallet_state_manager, wallet_2, asset_id
559
++ await time_out_assert(20, wallet_0.get_confirmed_balance, funds)
560
++
561
++ async with wallet_node_0.wallet_state_manager.lock:
562
++ cat_wallet_0, _ = await CATWallet.create_new_cat_wallet(
563
++ wallet_node_0.wallet_state_manager,
564
++ wallet_0,
565
++ {"identifier": "genesis_by_id"},
566
++ uint64(100),
567
++ DEFAULT_TX_CONFIG,
609
568
)
569
++ tx_records = await wallet_node_0.wallet_state_manager.tx_store.get_not_sent()
570
++ await full_node_api.process_transaction_records(records=tx_records)
610
571

611
-- assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_1.cat_info.limitations_program_hash
612
-- assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
572
++ await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 100)
573
++ await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 100)
613
574

614
-- cat_1_hash = await cat_wallet_1.get_new_inner_hash()
615
-- cat_2_hash = await cat_wallet_2.get_new_inner_hash()
575
++ assert cat_wallet_0.cat_info.limitations_program_hash is not None
576
++ asset_id = cat_wallet_0.get_asset_id()
616
577

617
-- tx_records = await cat_wallet_0.generate_signed_transaction(
618
-- [uint64(60), uint64(20)], [cat_1_hash, cat_2_hash], DEFAULT_TX_CONFIG
619
-- )
620
-- await wallet_0.wallet_state_manager.add_pending_transactions(tx_records)
621
-- await full_node_api.process_transaction_records(records=tx_records)
578
++ cat_wallet_1 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_1.wallet_state_manager, wallet_1, asset_id)
622
579

623
-- await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 20)
624
-- await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 20)
580
++ cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet_2, asset_id)
625
581

626
-- await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 60)
627
-- await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 60)
582
++ assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_1.cat_info.limitations_program_hash
583
++ assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
628
584

629
-- await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 20)
630
-- await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 20)
585
++ cat_1_hash = await cat_wallet_1.get_new_inner_hash()
586
++ cat_2_hash = await cat_wallet_2.get_new_inner_hash()
631
587

632
-- cat_hash = await cat_wallet_0.get_new_inner_hash()
588
++ tx_records = await cat_wallet_0.generate_signed_transaction(
589
++ [uint64(60), uint64(20)], [cat_1_hash, cat_2_hash], DEFAULT_TX_CONFIG
590
++ )
591
++ await wallet_0.wallet_state_manager.add_pending_transactions(tx_records)
592
++ await full_node_api.process_transaction_records(records=tx_records)
633
593

634
-- tx_records = await cat_wallet_1.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
635
-- await wallet_1.wallet_state_manager.add_pending_transactions(tx_records)
594
++ await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 20)
595
++ await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 20)
636
596

637
-- tx_records_2 = await cat_wallet_2.generate_signed_transaction([uint64(20)], [cat_hash], DEFAULT_TX_CONFIG)
638
-- await wallet_2.wallet_state_manager.add_pending_transactions(tx_records_2)
597
++ await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 60)
598
++ await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 60)
639
599

640
-- await full_node_api.process_transaction_records(records=[*tx_records, *tx_records_2])
600
++ await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 20)
601
++ await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 20)
641
602

642
-- await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 55)
643
-- await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 55)
603
++ cat_hash = await cat_wallet_0.get_new_inner_hash()
644
604

645
-- await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 45)
646
-- await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 45)
605
++ tx_records = await cat_wallet_1.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
606
++ await wallet_1.wallet_state_manager.add_pending_transactions(tx_records)
647
607

648
-- await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 0)
649
-- await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 0)
608
++ tx_records_2 = await cat_wallet_2.generate_signed_transaction([uint64(20)], [cat_hash], DEFAULT_TX_CONFIG)
609
++ await wallet_2.wallet_state_manager.add_pending_transactions(tx_records_2)
650
610

651
-- txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
652
-- # Test with Memo
653
-- tx_records_3: TransactionRecord = await cat_wallet_1.generate_signed_transaction(
654
-- [uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"Markus Walburg"]]
655
-- )
656
-- with pytest.raises(ValueError):
657
-- await cat_wallet_1.generate_signed_transaction(
658
-- [uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"too"], [b"many"], [b"memos"]]
659
-- )
660
--
661
-- await wallet_1.wallet_state_manager.add_pending_transactions(tx_records_3)
662
-- await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records_3)
663
-- txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
664
-- for tx in txs:
665
-- if tx.amount == 30:
666
-- memos = tx.get_memos()
667
-- assert len(memos) == 2 # One for tx, one for change
668
-- assert b"Markus Walburg" in [v for v_list in memos.values() for v in v_list]
669
-- assert list(memos.keys())[0] in [a.name() for a in tx.spend_bundle.additions()]
670
--
671
-- @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
672
-- @pytest.mark.parametrize("trusted", [True, False])
673
-- @pytest.mark.anyio
674
-- async def test_cat_max_amount_send(self, self_hostname, two_wallet_nodes, trusted):
675
-- num_blocks = 3
676
-- full_nodes, wallets, _ = two_wallet_nodes
677
-- full_node_api = full_nodes[0]
678
-- full_node_server = full_node_api.server
679
-- wallet_node, server_2 = wallets[0]
680
-- wallet_node_2, server_3 = wallets[1]
681
-- wallet = wallet_node.wallet_state_manager.main_wallet
682
--
683
-- ph = await wallet.get_new_puzzlehash()
684
-- if trusted:
685
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
686
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
687
-- else:
688
-- wallet_node.config["trusted_peers"] = {}
689
-- wallet_node_2.config["trusted_peers"] = {}
690
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
691
-- await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
692
--
693
-- for i in range(0, num_blocks):
694
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
695
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
696
--
697
-- funds = sum(
698
-- [
699
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
700
-- for i in range(1, num_blocks + 1)
701
-- ]
611
++ await full_node_api.process_transaction_records(records=[*tx_records, *tx_records_2])
612
++
613
++ await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 55)
614
++ await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 55)
615
++
616
++ await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 45)
617
++ await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 45)
618
++
619
++ await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 0)
620
++ await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 0)
621
++
622
++ txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
623
++ # Test with Memo
624
++ tx_records_3 = await cat_wallet_1.generate_signed_transaction(
625
++ [uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"Markus Walburg"]]
626
++ )
627
++ with pytest.raises(ValueError):
628
++ await cat_wallet_1.generate_signed_transaction(
629
++ [uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"too"], [b"many"], [b"memos"]]
702
630
)
703
631

704
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
632
++ await wallet_1.wallet_state_manager.add_pending_transactions(tx_records_3)
633
++ await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records_3)
634
++ txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
635
++ for tx in txs:
636
++ if tx.amount == 30:
637
++ memos = tx.get_memos()
638
++ assert len(memos) == 2 # One for tx, one for change
639
++ assert b"Markus Walburg" in [v for v_list in memos.values() for v in v_list]
640
++ assert tx.spend_bundle is not None
641
++ assert list(memos.keys())[0] in [a.name() for a in tx.spend_bundle.additions()]
642
++
705
643

706
-- async with wallet_node.wallet_state_manager.lock:
707
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
708
-- wallet_node.wallet_state_manager,
709
-- wallet,
710
-- {"identifier": "genesis_by_id"},
711
-- uint64(100000),
712
-- DEFAULT_TX_CONFIG,
713
-- )
714
-- tx_records: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
715
-- await full_node_api.process_transaction_records(records=tx_records)
644
++ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
645
++ @pytest.mark.parametrize("trusted", [True, False])
646
++ @pytest.mark.anyio
647
++ async def test_cat_max_amount_send(
648
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
649
++ ) -> None:
650
++ num_blocks = 3
651
++ full_nodes, wallets, _ = two_wallet_nodes
652
++ full_node_api = full_nodes[0]
653
++ full_node_server = full_node_api.server
654
++ wallet_node, server_2 = wallets[0]
655
++ wallet_node_2, server_3 = wallets[1]
656
++ wallet = wallet_node.wallet_state_manager.main_wallet
657
++
658
++ ph = await wallet.get_new_puzzlehash()
659
++ if trusted:
660
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
661
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
662
++ else:
663
++ wallet_node.config["trusted_peers"] = {}
664
++ wallet_node_2.config["trusted_peers"] = {}
665
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
666
++ await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
667
++
668
++ for _ in range(num_blocks):
669
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
670
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
716
671

717
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100000)
718
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100000)
672
++ funds = sum(
673
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
674
++ )
719
675

720
-- assert cat_wallet.cat_info.limitations_program_hash is not None
676
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
721
677

722
-- cat_2 = await cat_wallet.get_new_inner_puzzle()
723
-- cat_2_hash = cat_2.get_tree_hash()
724
-- amounts = []
725
-- puzzle_hashes = []
726
-- for i in range(1, 50):
727
-- amounts.append(uint64(i))
728
-- puzzle_hashes.append(cat_2_hash)
729
-- spent_coint = (await cat_wallet.get_cat_spendable_coins())[0].coin
730
-- tx_records = await cat_wallet.generate_signed_transaction(
731
-- amounts, puzzle_hashes, DEFAULT_TX_CONFIG, coins={spent_coint}
732
-- )
733
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
734
-- await full_node_api.process_transaction_records(records=tx_records)
735
--
736
-- await asyncio.sleep(2)
737
--
738
-- async def check_all_there():
739
-- spendable = await cat_wallet.get_cat_spendable_coins()
740
-- spendable_name_set = set()
741
-- for record in spendable:
742
-- spendable_name_set.add(record.coin.name())
743
-- puzzle_hash = construct_cat_puzzle(
744
-- CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2
745
-- ).get_tree_hash()
746
-- for i in range(1, 50):
747
-- coin = Coin(spent_coint.name(), puzzle_hash, i)
748
-- if coin.name() not in spendable_name_set:
749
-- return False
750
-- return True
751
--
752
-- await time_out_assert(20, check_all_there, True)
753
-- await asyncio.sleep(5)
754
-- max_sent_amount = await cat_wallet.get_max_send_amount()
755
--
756
-- # 1) Generate transaction that is under the limit
757
-- [transaction_record] = await cat_wallet.generate_signed_transaction(
758
-- [max_sent_amount - 1],
759
-- [ph],
678
++ async with wallet_node.wallet_state_manager.lock:
679
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
680
++ wallet_node.wallet_state_manager,
681
++ wallet,
682
++ {"identifier": "genesis_by_id"},
683
++ uint64(100000),
760
684
DEFAULT_TX_CONFIG,
761
685
)
686
++ tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
687
++ await full_node_api.process_transaction_records(records=tx_records)
762
688

763
-- assert transaction_record.amount == uint64(max_sent_amount - 1)
689
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100000)
690
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100000)
764
691

765
-- # 2) Generate transaction that is equal to limit
766
-- [transaction_record] = await cat_wallet.generate_signed_transaction(
767
-- [max_sent_amount],
768
-- [ph],
769
-- DEFAULT_TX_CONFIG,
770
-- )
692
++ assert cat_wallet.cat_info.limitations_program_hash is not None
771
693

772
-- assert transaction_record.amount == uint64(max_sent_amount)
773
--
774
-- # 3) Generate transaction that is greater than limit
775
-- with pytest.raises(ValueError):
776
-- await cat_wallet.generate_signed_transaction(
777
-- [max_sent_amount + 1],
778
-- [ph],
779
-- DEFAULT_TX_CONFIG,
780
-- )
781
--
782
-- @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
783
-- @pytest.mark.parametrize("trusted", [True, False])
784
-- @pytest.mark.parametrize("autodiscovery", [True, False])
785
-- @pytest.mark.anyio
786
-- async def test_cat_hint(self, self_hostname, two_wallet_nodes, trusted, autodiscovery):
787
-- num_blocks = 3
788
-- full_nodes, wallets, _ = two_wallet_nodes
789
-- full_node_api = full_nodes[0]
790
-- full_node_server = full_node_api.server
791
-- wallet_node, server_2 = wallets[0]
792
-- wallet_node_2, server_3 = wallets[1]
793
-- wallet = wallet_node.wallet_state_manager.main_wallet
794
-- wallet2 = wallet_node_2.wallet_state_manager.main_wallet
795
--
796
-- ph = await wallet.get_new_puzzlehash()
797
-- if trusted:
798
-- wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
799
-- wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
800
-- else:
801
-- wallet_node.config["trusted_peers"] = {}
802
-- wallet_node_2.config["trusted_peers"] = {}
803
-- wallet_node.config["automatically_add_unknown_cats"] = autodiscovery
804
-- wallet_node_2.config["automatically_add_unknown_cats"] = autodiscovery
805
-- await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
806
-- await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
807
--
808
-- for i in range(0, num_blocks):
809
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
810
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
811
--
812
-- funds = sum(
813
-- [
814
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
815
-- for i in range(1, num_blocks + 1)
816
-- ]
817
-- )
694
++ cat_2 = await cat_wallet.get_new_inner_puzzle()
695
++ cat_2_hash = cat_2.get_tree_hash()
696
++ amounts = []
697
++ puzzle_hashes = []
698
++ for i in range(1, 50):
699
++ amounts.append(uint64(i))
700
++ puzzle_hashes.append(cat_2_hash)
701
++ spent_coint = (await cat_wallet.get_cat_spendable_coins())[0].coin
702
++ tx_records = await cat_wallet.generate_signed_transaction(
703
++ amounts, puzzle_hashes, DEFAULT_TX_CONFIG, coins={spent_coint}
704
++ )
705
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
706
++ await full_node_api.process_transaction_records(records=tx_records)
818
707

819
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
820
--
821
-- async with wallet_node.wallet_state_manager.lock:
822
-- cat_wallet, _ = await CATWallet.create_new_cat_wallet(
823
-- wallet_node.wallet_state_manager,
824
-- wallet,
825
-- {"identifier": "genesis_by_id"},
826
-- uint64(100),
827
-- DEFAULT_TX_CONFIG,
828
-- )
829
-- tx_records: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
830
-- await full_node_api.process_transaction_records(records=tx_records)
831
--
832
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
833
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
834
-- assert cat_wallet.cat_info.limitations_program_hash is not None
835
--
836
-- cat_2_hash = await wallet2.get_new_puzzlehash()
837
-- tx_records = await cat_wallet.generate_signed_transaction(
838
-- [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
839
-- )
708
++ await asyncio.sleep(2)
709
++
710
++ async def check_all_there() -> bool:
711
++ spendable = await cat_wallet.get_cat_spendable_coins()
712
++ spendable_name_set = set()
713
++ for record in spendable:
714
++ spendable_name_set.add(record.coin.name())
715
++ puzzle_hash = construct_cat_puzzle(CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2).get_tree_hash()
716
++ for i in range(1, 50):
717
++ coin = Coin(spent_coint.name(), puzzle_hash, i)
718
++ if coin.name() not in spendable_name_set:
719
++ return False
720
++ return True
721
++
722
++ await time_out_assert(20, check_all_there, True)
723
++ await asyncio.sleep(5)
724
++ max_sent_amount = await cat_wallet.get_max_send_amount()
725
++
726
++ # 1) Generate transaction that is under the limit
727
++ [transaction_record] = await cat_wallet.generate_signed_transaction(
728
++ [uint64(max_sent_amount - 1)], [ph], DEFAULT_TX_CONFIG
729
++ )
730
++ assert transaction_record.amount == uint64(max_sent_amount - 1)
840
731

841
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
732
++ # 2) Generate transaction that is equal to limit
733
++ [transaction_record] = await cat_wallet.generate_signed_transaction(
734
++ [uint64(max_sent_amount)], [ph], DEFAULT_TX_CONFIG
735
++ )
736
++ assert transaction_record.amount == uint64(max_sent_amount)
737
++
738
++ # 3) Generate transaction that is greater than limit
739
++ with pytest.raises(ValueError):
740
++ await cat_wallet.generate_signed_transaction([uint64(max_sent_amount + 1)], [ph], DEFAULT_TX_CONFIG)
842
741

843
-- await full_node_api.process_transaction_records(records=tx_records)
844
742

845
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
846
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
743
++ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
744
++ @pytest.mark.parametrize("trusted", [True, False])
745
++ @pytest.mark.parametrize("autodiscovery", [True, False])
746
++ @pytest.mark.anyio
747
++ async def test_cat_hint(
748
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool, autodiscovery: bool
749
++ ) -> None:
750
++ num_blocks = 3
751
++ full_nodes, wallets, _ = two_wallet_nodes
752
++ full_node_api = full_nodes[0]
753
++ full_node_server = full_node_api.server
754
++ wallet_node, server_2 = wallets[0]
755
++ wallet_node_2, server_3 = wallets[1]
756
++ wallet = wallet_node.wallet_state_manager.main_wallet
757
++ wallet2 = wallet_node_2.wallet_state_manager.main_wallet
758
++
759
++ ph = await wallet.get_new_puzzlehash()
760
++ if trusted:
761
++ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
762
++ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
763
++ else:
764
++ wallet_node.config["trusted_peers"] = {}
765
++ wallet_node_2.config["trusted_peers"] = {}
766
++ wallet_node.config["automatically_add_unknown_cats"] = autodiscovery
767
++ wallet_node_2.config["automatically_add_unknown_cats"] = autodiscovery
768
++ await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
769
++ await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
770
++
771
++ for _ in range(num_blocks):
772
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
773
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
847
774

848
-- if autodiscovery:
849
-- # Autodiscovery enabled: test that wallet was created at this point
850
-- await time_out_assert(20, check_wallets, 2, wallet_node_2)
851
-- else:
852
-- # Autodiscovery disabled: test that no wallet was created
853
-- await time_out_assert(20, check_wallets, 1, wallet_node_2)
775
++ funds = sum(
776
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
777
++ )
854
778

855
-- # Then we update the wallet's default CATs
856
-- wallet_node_2.wallet_state_manager.default_cats = {
857
-- cat_wallet.cat_info.limitations_program_hash.hex(): {
858
-- "asset_id": cat_wallet.cat_info.limitations_program_hash.hex(),
859
-- "name": "Test",
860
-- "symbol": "TST",
861
-- }
862
-- }
779
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
863
780

864
-- # Then we send another transaction
865
-- tx_records = await cat_wallet.generate_signed_transaction(
866
-- [uint64(10)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
781
++ async with wallet_node.wallet_state_manager.lock:
782
++ cat_wallet, _ = await CATWallet.create_new_cat_wallet(
783
++ wallet_node.wallet_state_manager,
784
++ wallet,
785
++ {"identifier": "genesis_by_id"},
786
++ uint64(100),
787
++ DEFAULT_TX_CONFIG,
867
788
)
789
++ tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
790
++ await full_node_api.process_transaction_records(records=tx_records)
868
791

869
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
792
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
793
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
794
++ assert cat_wallet.cat_info.limitations_program_hash is not None
870
795

871
-- await full_node_api.process_transaction_records(records=tx_records)
796
++ cat_2_hash = await wallet2.get_new_puzzlehash()
797
++ tx_records = await cat_wallet.generate_signed_transaction(
798
++ [uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
799
++ )
800
++
801
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
802
++
803
++ await full_node_api.process_transaction_records(records=tx_records)
872
804

873
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 30)
874
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 30)
805
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
806
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
875
807

876
-- # Now we check that another wallet WAS created, even if autodiscovery was disabled
808
++ if autodiscovery:
809
++ # Autodiscovery enabled: test that wallet was created at this point
877
810
await time_out_assert(20, check_wallets, 2, wallet_node_2)
878
-- cat_wallet_2 = wallet_node_2.wallet_state_manager.wallets[2]
811
++ else:
812
++ # Autodiscovery disabled: test that no wallet was created
813
++ await time_out_assert(20, check_wallets, 1, wallet_node_2)
814
++
815
++ # Then we update the wallet's default CATs
816
++ wallet_node_2.wallet_state_manager.default_cats = {
817
++ cat_wallet.cat_info.limitations_program_hash.hex(): {
818
++ "asset_id": cat_wallet.cat_info.limitations_program_hash.hex(),
819
++ "name": "Test",
820
++ "symbol": "TST",
821
++ }
822
++ }
823
++
824
++ # Then we send another transaction
825
++ tx_records = await cat_wallet.generate_signed_transaction(
826
++ [uint64(10)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
827
++ )
828
++
829
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
830
++
831
++ await full_node_api.process_transaction_records(records=tx_records)
832
++
833
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 30)
834
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 30)
879
835

880
-- # Previous balance + balance that triggered creation in case of disabled autodiscovery
881
-- await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 70)
882
-- await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 70)
836
++ # Now we check that another wallet WAS created, even if autodiscovery was disabled
837
++ await time_out_assert(20, check_wallets, 2, wallet_node_2)
838
++ cat_wallet_2 = wallet_node_2.wallet_state_manager.wallets[uint32(2)]
839
++ assert isinstance(cat_wallet_2, CATWallet)
883
840

884
-- cat_hash = await cat_wallet.get_new_inner_hash()
885
-- tx_records = await cat_wallet_2.generate_signed_transaction([uint64(5)], [cat_hash], DEFAULT_TX_CONFIG)
886
-- await wallet.wallet_state_manager.add_pending_transactions(tx_records)
841
++ # Previous balance + balance that triggered creation in case of disabled autodiscovery
842
++ await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 70)
843
++ await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 70)
887
844

888
-- await full_node_api.process_transaction_records(records=tx_records)
845
++ cat_hash = await cat_wallet.get_new_inner_hash()
846
++ tx_records = await cat_wallet_2.generate_signed_transaction([uint64(5)], [cat_hash], DEFAULT_TX_CONFIG)
847
++ await wallet.wallet_state_manager.add_pending_transactions(tx_records)
889
848

890
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, 35)
891
-- await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 35)
849
++ await full_node_api.process_transaction_records(records=tx_records)
892
850

893
-- @pytest.mark.parametrize(
894
-- "trusted",
895
-- [True, False],
851
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, 35)
852
++ await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 35)
853
++
854
++
855
++ @pytest.mark.parametrize("trusted", [True, False])
856
++ @pytest.mark.anyio
857
++ async def test_cat_change_detection(
858
++ self_hostname: str, one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices, trusted: bool
859
++ ) -> None:
860
++ num_blocks = 1
861
++ full_nodes, wallets, bt = one_wallet_and_one_simulator_services
862
++ full_node_api = full_nodes[0]._api
863
++ full_node_server = full_node_api.full_node.server
864
++ wallet_service_0 = wallets[0]
865
++ wallet_node_0 = wallet_service_0._node
866
++ wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
867
++
868
++ assert wallet_service_0.rpc_server is not None
869
++
870
++ client_0 = await WalletRpcClient.create(
871
++ bt.config["self_hostname"],
872
++ wallet_service_0.rpc_server.listen_port,
873
++ wallet_service_0.root_path,
874
++ wallet_service_0.config,
896
875
)
897
-- @pytest.mark.anyio
898
-- async def test_cat_change_detection(
899
-- self, self_hostname: str, one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices, trusted: bool
900
-- ) -> None:
901
-- num_blocks = 1
902
-- full_nodes, wallets, bt = one_wallet_and_one_simulator_services
903
-- full_node_api: FullNodeSimulator = full_nodes[0]._api
904
-- full_node_server = full_node_api.full_node.server
905
-- wallet_service_0 = wallets[0]
906
-- wallet_node_0 = wallet_service_0._node
907
-- wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
908
--
909
-- assert wallet_service_0.rpc_server is not None
910
--
911
-- client_0 = await WalletRpcClient.create(
912
-- bt.config["self_hostname"],
913
-- wallet_service_0.rpc_server.listen_port,
914
-- wallet_service_0.root_path,
915
-- wallet_service_0.config,
916
-- )
917
-- wallet_node_0.config["automatically_add_unknown_cats"] = True
918
--
919
-- if trusted:
920
-- wallet_node_0.config["trusted_peers"] = {
921
-- full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
922
-- }
923
-- else:
924
-- wallet_node_0.config["trusted_peers"] = {}
925
--
926
-- await wallet_node_0.server.start_client(PeerInfo(self_hostname, uint16(full_node_server.get_port())), None)
927
-- await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
928
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
929
--
930
-- # Mint CAT to ourselves, immediately spend it to an unhinted puzzle hash that we have manually added to the DB
931
-- # We should pick up this coin as balance even though it is unhinted because it is "change"
932
-- intermediate_sk_un = master_sk_to_wallet_sk_unhardened_intermediate(
933
-- wallet_node_0.wallet_state_manager.private_key
934
-- )
935
-- pubkey_unhardened = _derive_path_unhardened(intermediate_sk_un, [100000000]).get_g1()
936
-- inner_puzhash = puzzle_hash_for_pk(pubkey_unhardened)
937
-- puzzlehash_unhardened = construct_cat_puzzle(
938
-- CAT_MOD, Program.to(None).get_tree_hash(), inner_puzhash
939
-- ).get_tree_hash_precalc(inner_puzhash)
940
-- change_derivation = DerivationRecord(
941
-- uint32(0),
942
-- puzzlehash_unhardened,
943
-- pubkey_unhardened,
944
-- WalletType.CAT,
945
-- uint32(2),
946
-- False,
947
-- )
948
-- # Insert the derivation record before the wallet exists so that it is not subscribed to
949
-- await wallet_node_0.wallet_state_manager.puzzle_store.add_derivation_paths([change_derivation])
950
-- our_puzzle: Program = await wallet_0.get_new_puzzle()
951
-- cat_puzzle: Program = construct_cat_puzzle(
952
-- CAT_MOD,
953
-- Program.to(None).get_tree_hash(),
954
-- Program.to(1),
955
-- )
956
-- addr = encode_puzzle_hash(cat_puzzle.get_tree_hash(), "txch")
957
-- cat_amount_0 = uint64(100)
958
-- cat_amount_1 = uint64(5)
959
--
960
-- tx = await client_0.send_transaction(1, cat_amount_0, addr, DEFAULT_TX_CONFIG)
961
-- spend_bundle = tx.spend_bundle
962
-- assert spend_bundle is not None
963
--
964
-- await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
965
-- await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
966
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
967
--
968
-- # Do the eve spend back to our wallet and add the CR layer
969
-- cat_coin = next(c for c in spend_bundle.additions() if c.amount == cat_amount_0)
970
-- next_coin = Coin(
971
-- cat_coin.name(),
972
-- construct_cat_puzzle(
973
-- CAT_MOD,
974
-- Program.to(None).get_tree_hash(),
975
-- our_puzzle,
976
-- ).get_tree_hash(),
977
-- cat_amount_0,
978
-- )
979
-- eve_spend = await wallet_node_0.wallet_state_manager.sign_transaction(
980
-- [
981
-- make_spend(
982
-- cat_coin,
983
-- cat_puzzle,
984
-- Program.to(
876
++ wallet_node_0.config["automatically_add_unknown_cats"] = True
877
++
878
++ if trusted:
879
++ wallet_node_0.config["trusted_peers"] = {
880
++ full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
881
++ }
882
++ else:
883
++ wallet_node_0.config["trusted_peers"] = {}
884
++
885
++ await wallet_node_0.server.start_client(PeerInfo(self_hostname, uint16(full_node_server.get_port())), None)
886
++ await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
887
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
888
++
889
++ # Mint CAT to ourselves, immediately spend it to an unhinted puzzle hash that we have manually added to the DB
890
++ # We should pick up this coin as balance even though it is unhinted because it is "change"
891
++ intermediate_sk_un = master_sk_to_wallet_sk_unhardened_intermediate(wallet_node_0.wallet_state_manager.private_key)
892
++ pubkey_unhardened = _derive_path_unhardened(intermediate_sk_un, [100000000]).get_g1()
893
++ inner_puzhash = puzzle_hash_for_pk(pubkey_unhardened)
894
++ puzzlehash_unhardened = construct_cat_puzzle(
895
++ CAT_MOD, Program.to(None).get_tree_hash(), inner_puzhash
896
++ ).get_tree_hash_precalc(inner_puzhash)
897
++ change_derivation = DerivationRecord(
898
++ uint32(0), puzzlehash_unhardened, pubkey_unhardened, WalletType.CAT, uint32(2), False
899
++ )
900
++ # Insert the derivation record before the wallet exists so that it is not subscribed to
901
++ await wallet_node_0.wallet_state_manager.puzzle_store.add_derivation_paths([change_derivation])
902
++ our_puzzle = await wallet_0.get_new_puzzle()
903
++ cat_puzzle = construct_cat_puzzle(
904
++ CAT_MOD,
905
++ Program.to(None).get_tree_hash(),
906
++ Program.to(1),
907
++ )
908
++ addr = encode_puzzle_hash(cat_puzzle.get_tree_hash(), "txch")
909
++ cat_amount_0 = uint64(100)
910
++ cat_amount_1 = uint64(5)
911
++
912
++ tx = await client_0.send_transaction(1, cat_amount_0, addr, DEFAULT_TX_CONFIG)
913
++ spend_bundle = tx.spend_bundle
914
++ assert spend_bundle is not None
915
++
916
++ await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
917
++ await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
918
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
919
++
920
++ # Do the eve spend back to our wallet and add the CR layer
921
++ cat_coin = next(c for c in spend_bundle.additions() if c.amount == cat_amount_0)
922
++ next_coin = Coin(
923
++ cat_coin.name(),
924
++ construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), our_puzzle).get_tree_hash(),
925
++ cat_amount_0,
926
++ )
927
++ eve_spend = await wallet_node_0.wallet_state_manager.sign_transaction(
928
++ [
929
++ make_spend(
930
++ cat_coin,
931
++ cat_puzzle,
932
++ Program.to(
933
++ [
934
++ Program.to(
935
++ [
936
++ [51, our_puzzle.get_tree_hash(), cat_amount_0, [our_puzzle.get_tree_hash()]],
937
++ [51, None, -113, None, None],
938
++ ]
939
++ ),
940
++ None,
941
++ cat_coin.name(),
942
++ coin_as_list(cat_coin),
943
++ [cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_coin.amount],
944
++ 0,
945
++ 0,
946
++ ]
947
++ ),
948
++ ),
949
++ make_spend(
950
++ next_coin,
951
++ construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), our_puzzle),
952
++ Program.to(
953
++ [
985
954
[
986
-- Program.to(
955
++ None,
956
++ (
957
++ 1,
987
958
[
988
-- [
989
-- 51,
990
-- our_puzzle.get_tree_hash(),
991
-- cat_amount_0,
992
-- [our_puzzle.get_tree_hash()],
993
-- ],
994
-- [51, None, -113, None, None],
995
-- ]
959
++ [51, inner_puzhash, cat_amount_1],
960
++ [51, bytes32([0] * 32), cat_amount_0 - cat_amount_1],
961
++ ],
996
962
),
997
963
None,
998
-- cat_coin.name(),
999
-- coin_as_list(cat_coin),
1000
-- [cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_coin.amount],
1001
-- 0,
1002
-- 0,
1003
-- ]
1004
-- ),
964
++ ],
965
++ LineageProof(
966
++ cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_amount_0
967
++ ).to_program(),
968
++ next_coin.name(),
969
++ coin_as_list(next_coin),
970
++ [next_coin.parent_coin_info, our_puzzle.get_tree_hash(), next_coin.amount],
971
++ 0,
972
++ 0,
973
++ ]
1005
974
),
1006
-- make_spend(
1007
-- next_coin,
1008
-- construct_cat_puzzle(
1009
-- CAT_MOD,
1010
-- Program.to(None).get_tree_hash(),
1011
-- our_puzzle,
1012
-- ),
1013
-- Program.to(
1014
-- [
1015
-- [
1016
-- None,
1017
-- (
1018
-- 1,
1019
-- [
1020
-- [51, inner_puzhash, cat_amount_1],
1021
-- [51, bytes32([0] * 32), cat_amount_0 - cat_amount_1],
1022
-- ],
1023
-- ),
1024
-- None,
1025
-- ],
1026
-- LineageProof(
1027
-- cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_amount_0
1028
-- ).to_program(),
1029
-- next_coin.name(),
1030
-- coin_as_list(next_coin),
1031
-- [next_coin.parent_coin_info, our_puzzle.get_tree_hash(), next_coin.amount],
1032
-- 0,
1033
-- 0,
1034
-- ]
1035
-- ),
1036
-- ),
1037
-- ],
1038
-- )
1039
-- await client_0.push_tx(eve_spend)
1040
-- await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, eve_spend.name())
1041
-- await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
1042
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
975
++ ),
976
++ ],
977
++ )
978
++ await client_0.push_tx(eve_spend)
979
++ await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, eve_spend.name())
980
++ await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
981
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
1043
982

1044
-- await time_out_assert(20, check_wallets, 2, wallet_node_0)
1045
-- cat_wallet = wallet_node_0.wallet_state_manager.wallets[uint32(2)]
1046
-- await time_out_assert(20, cat_wallet.get_confirmed_balance, cat_amount_1)
1047
-- assert not full_node_api.full_node.subscriptions.has_ph_subscription(puzzlehash_unhardened)
983
++ await time_out_assert(20, check_wallets, 2, wallet_node_0)
984
++ cat_wallet = wallet_node_0.wallet_state_manager.wallets[uint32(2)]
985
++ await time_out_assert(20, cat_wallet.get_confirmed_balance, cat_amount_1)
986
++ assert not full_node_api.full_node.subscriptions.has_puzzle_subscription(puzzlehash_unhardened)
1048
987

1049
988

1050
989
@pytest.mark.anyio
1051
990
async def test_unacknowledged_cat_table() -> None:
1052
991
db_name = Path(tempfile.TemporaryDirectory().name).joinpath("test.sqlite")
1053
992
db_name.parent.mkdir(parents=True, exist_ok=True)
1054
-- async with DBWrapper2.managed(
1055
-- database=db_name,
1056
-- ) as db_wrapper:
993
++ async with DBWrapper2.managed(database=db_name) as db_wrapper:
1057
994
interested_store = await WalletInterestedStore.create(db_wrapper)
1058
995

1059
996
def asset_id(i: int) -> bytes32:
@@@ -1062,22 -1062,22 +999,10 @@@
1062
999
def coin_state(i: int) -> CoinState:
1063
1000
return CoinState(Coin(bytes32([0] * 32), bytes32([0] * 32), uint64(i)), None, None)
1064
1001

1065
-- await interested_store.add_unacknowledged_coin_state(
1066
-- asset_id(0),
1067
-- coin_state(0),
1068
-- None,
1069
-- )
1070
-- await interested_store.add_unacknowledged_coin_state(
1071
-- asset_id(1),
1072
-- coin_state(1),
1073
-- 100,
1074
-- )
1002
++ await interested_store.add_unacknowledged_coin_state(asset_id(0), coin_state(0), None)
1003
++ await interested_store.add_unacknowledged_coin_state(asset_id(1), coin_state(1), 100)
1075
1004
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(0)) == [(coin_state(0), 0)]
1076
-- await interested_store.add_unacknowledged_coin_state(
1077
-- asset_id(0),
1078
-- coin_state(0),
1079
-- None,
1080
-- )
1005
++ await interested_store.add_unacknowledged_coin_state(asset_id(0), coin_state(0), None)
1081
1006
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(0)) == [(coin_state(0), 0)]
1082
1007
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(1)) == [(coin_state(1), 100)]
1083
1008
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(2)) == []
tests/wallet/cat_wallet/test_offer_lifecycle.py CHANGED
@@@ -1,17 -1,17 +1,17 @@@
1
1
from __future__ import annotations
2
2

3
3
from dataclasses import replace
4
-- from typing import Any, Dict, List, Optional
4
++ from typing import Any, Dict, List, Optional, cast
5
5

6
6
import pytest
7
7
from chia_rs import G2Element
8
8

9
-- from chia.clvm.spend_sim import sim_and_client
9
++ from chia.clvm.spend_sim import CostLogger, SimClient, SpendSim, sim_and_client
10
10
from chia.types.blockchain_format.coin import Coin
11
11
from chia.types.blockchain_format.program import Program
12
12
from chia.types.blockchain_format.serialized_program import SerializedProgram
13
13
from chia.types.blockchain_format.sized_bytes import bytes32
14
-- from chia.types.coin_spend import CoinSpend, make_spend
14
++ from chia.types.coin_spend import make_spend
15
15
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
16
16
from chia.types.spend_bundle import SpendBundle
17
17
from chia.util.ints import uint64
@@@ -21,11 -21,11 +21,11 @@@ from chia.wallet.cat_wallet.cat_utils i
21
21
construct_cat_puzzle,
22
22
unsigned_spend_bundle_for_spendable_cats,
23
23
)
24
-- from chia.wallet.conditions import AssertCoinAnnouncement, ConditionValidTimes
24
++ from chia.wallet.conditions import AssertPuzzleAnnouncement, ConditionValidTimes
25
25
from chia.wallet.outer_puzzles import AssetType
26
26
from chia.wallet.payment import Payment
27
27
from chia.wallet.puzzle_drivers import PuzzleInfo
28
-- from chia.wallet.trading.offer import OFFER_MOD, NotarizedPayment, Offer
28
++ from chia.wallet.trading.offer import OFFER_MOD, Offer
29
29

30
30
acs = Program.to(1)
31
31
acs_ph = acs.get_tree_hash()
@@@ -33,7 -33,7 +33,8 @@@
33
33

34
34
# Some methods mapping strings to CATs
35
35
def str_to_tail(tail_str: str) -> Program:
36
-- return Program.to([3, [], [1, tail_str], []])
36
++ # TODO: Remove cast when we improve typing
37
++ return cast(Program, Program.to([3, [], [1, tail_str], []]))
37
38

38
39

39
40
def str_to_tail_hash(tail_str: str) -> bytes32:
@@@ -46,24 -46,24 +47,22 @@@ def str_to_cat_hash(tail_str: str) -> b
46
47

47
48
# This method takes a dictionary of strings mapping to amounts and generates the appropriate CAT/XCH coins
48
49
async def generate_coins(
49
-- sim,
50
-- sim_client,
51
-- requested_coins: Dict[Optional[str], List[uint64]],
50
++ sim: SpendSim, sim_client: SimClient, requested_coins: Dict[Optional[str], List[int]]
52
51
) -> Dict[Optional[str], List[Coin]]:
53
52
await sim.farm_block(acs_ph)
54
-- parent_coin: Coin = [cr.coin for cr in await sim_client.get_coin_records_by_puzzle_hash(acs_ph)][0]
53
++ parent_coin = [cr.coin for cr in await sim_client.get_coin_records_by_puzzle_hash(acs_ph)][0]
55
54

56
55
# We need to gather a list of initial coins to create as well as spends that do the eve spend for every CAT
57
-- payments: List[Payment] = []
58
-- cat_bundles: List[SpendBundle] = []
56
++ payments = []
57
++ cat_bundles = []
59
58
for tail_str, amounts in requested_coins.items():
60
59
for amount in amounts:
61
60
if tail_str:
62
-- tail: Program = str_to_tail(tail_str) # Making a fake but unique TAIL
61
++ tail = str_to_tail(tail_str) # Making a fake but unique TAIL
63
62
tail_hash = tail.get_tree_hash()
64
-- cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail_hash, acs)
63
++ cat_puzzle = construct_cat_puzzle(CAT_MOD, tail_hash, acs)
65
64
cat_puzzle_hash = cat_puzzle.get_tree_hash()
66
-- payments.append(Payment(cat_puzzle_hash, amount))
65
++ payments.append(Payment(cat_puzzle_hash, uint64(amount)))
67
66
cat_bundles.append(
68
67
unsigned_spend_bundle_for_spendable_cats(
69
68
CAT_MOD,
@@@ -78,18 -78,18 +77,11 @@@
78
77
)
79
78
)
80
79
else:
81
-- payments.append(Payment(acs_ph, amount))
80
++ payments.append(Payment(acs_ph, uint64(amount)))
82
81

83
82
# This bundle creates all of the initial coins
84
83
parent_bundle = SpendBundle(
85
-- [
86
-- make_spend(
87
-- parent_coin,
88
-- acs,
89
-- Program.to([[51, p.puzzle_hash, p.amount] for p in payments]),
90
-- )
91
-- ],
92
-- G2Element(),
84
++ [make_spend(parent_coin, acs, Program.to([[51, p.puzzle_hash, p.amount] for p in payments]))], G2Element()
93
85
)
94
86

95
87
# Then we aggregate it with all of the eve spends
@@@ -101,7 -101,7 +93,7 @@@
101
93
for tail_str, _ in requested_coins.items():
102
94
if tail_str:
103
95
tail_hash = str_to_tail_hash(tail_str)
104
-- cat_ph: bytes32 = construct_cat_puzzle(CAT_MOD, tail_hash, acs).get_tree_hash()
96
++ cat_ph = construct_cat_puzzle(CAT_MOD, tail_hash, acs).get_tree_hash()
105
97
coin_dict[tail_str] = [
106
98
cr.coin for cr in await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)
107
99
]
@@@ -123,14 -123,14 +115,14 @@@
123
115
# but doesn't bother with non-offer announcements
124
116
def generate_secure_bundle(
125
117
selected_coins: List[Coin],
126
-- announcements: List[AssertCoinAnnouncement],
118
++ announcements: List[AssertPuzzleAnnouncement],
127
119
offered_amount: uint64,
128
120
tail_str: Optional[str] = None,
129
121
) -> SpendBundle:
130
-- announcement_assertions: List[Program] = [a.to_program() for a in announcements]
131
-- selected_coin_amount: int = sum([c.amount for c in selected_coins])
132
-- non_primaries: List[Coin] = [] if len(selected_coins) < 2 else selected_coins[1:]
133
-- inner_solution: List[List] = [
122
++ announcement_assertions = [a.to_program() for a in announcements]
123
++ selected_coin_amount = sum([c.amount for c in selected_coins])
124
++ non_primaries = [] if len(selected_coins) < 2 else selected_coins[1:]
125
++ inner_solution: List[List[Any]] = [
134
126
[51, Offer.ph(), offered_amount], # Offered coin
135
127
[51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change
136
128
*announcement_assertions,
@@@ -149,7 -149,7 +141,7 @@@
149
141
G2Element(),
150
142
)
151
143
else:
152
-- spendable_cats: List[SpendableCAT] = [
144
++ spendable_cats = [
153
145
SpendableCAT(
154
146
c,
155
147
str_to_tail_hash(tail_str),
@@@ -168,180 -168,180 +160,135 @@@
168
160
return bundle
169
161

170
162

171
-- class TestOfferLifecycle:
172
-- @pytest.mark.anyio()
173
-- async def test_complex_offer(self, cost_logger):
174
-- async with sim_and_client() as (sim, sim_client):
175
-- coins_needed: Dict[Optional[str], List[int]] = {
176
-- None: [500, 400, 300],
177
-- "red": [250, 100],
178
-- "blue": [3000],
179
-- }
180
-- all_coins: Dict[Optional[str], List[Coin]] = await generate_coins(sim, sim_client, coins_needed)
181
-- chia_coins: List[Coin] = all_coins[None]
182
-- red_coins: List[Coin] = all_coins["red"]
183
-- blue_coins: List[Coin] = all_coins["blue"]
184
--
185
-- driver_dict: Dict[bytes32, PuzzleInfo] = {
186
-- str_to_tail_hash("red"): PuzzleInfo(
187
-- {"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("red").hex()}
188
-- ),
189
-- str_to_tail_hash("blue"): PuzzleInfo(
190
-- {"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("blue").hex()}
191
-- ),
192
-- }
193
--
194
-- driver_dict_as_infos: Dict[str, Any] = {}
195
-- for key, value in driver_dict.items():
196
-- driver_dict_as_infos[key.hex()] = value.info
197
--
198
-- # Create an XCH Offer for RED
199
-- chia_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
200
-- str_to_tail_hash("red"): [
201
-- Payment(acs_ph, 100, [b"memo"]),
202
-- Payment(acs_ph, 200, [b"memo"]),
203
-- ]
204
-- }
205
--
206
-- chia_requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments(
207
-- chia_requested_payments, chia_coins
208
-- )
209
-- chia_announcements: List[AssertCoinAnnouncement] = Offer.calculate_announcements(
210
-- chia_requested_payments, driver_dict
211
-- )
212
-- chia_secured_bundle: SpendBundle = generate_secure_bundle(chia_coins, chia_announcements, 1000)
213
-- chia_offer = Offer(chia_requested_payments, chia_secured_bundle, driver_dict)
214
-- assert not chia_offer.is_valid()
215
--
216
-- # Create a RED Offer for XCH
217
-- red_coins_1 = red_coins[0:1]
218
-- red_coins_2 = red_coins[1:]
219
-- red_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
220
-- None: [
221
-- Payment(acs_ph, 300, [b"red memo"]),
222
-- Payment(acs_ph, 350, [b"red memo"]),
223
-- ]
224
-- }
225
--
226
-- red_requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments(
227
-- red_requested_payments, red_coins_1
228
-- )
229
-- red_announcements: List[AssertCoinAnnouncement] = Offer.calculate_announcements(
230
-- red_requested_payments, driver_dict
231
-- )
232
-- red_secured_bundle: SpendBundle = generate_secure_bundle(
233
-- red_coins_1, red_announcements, sum([c.amount for c in red_coins_1]), tail_str="red"
234
-- )
235
-- red_offer = Offer(red_requested_payments, red_secured_bundle, driver_dict)
236
-- assert not red_offer.is_valid()
237
--
238
-- red_requested_payments_2: Dict[Optional[bytes32], List[Payment]] = {
239
-- None: [
240
-- Payment(acs_ph, 50, [b"red memo"]),
241
-- ]
242
-- }
243
--
244
-- red_requested_payments_2: Dict[Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments(
245
-- red_requested_payments_2, red_coins_2
246
-- )
247
-- red_announcements_2: List[AssertCoinAnnouncement] = Offer.calculate_announcements(
248
-- red_requested_payments_2, driver_dict
249
-- )
250
-- red_secured_bundle_2: SpendBundle = generate_secure_bundle(
251
-- red_coins_2, red_announcements_2, sum([c.amount for c in red_coins_2]), tail_str="red"
252
-- )
253
-- red_offer_2 = Offer(red_requested_payments_2, red_secured_bundle_2, driver_dict)
254
-- assert not red_offer_2.is_valid()
255
--
256
-- # Test aggregation of offers
257
-- new_offer = Offer.aggregate([chia_offer, red_offer, red_offer_2])
258
-- assert new_offer.get_offered_amounts() == {None: 1000, str_to_tail_hash("red"): 350}
259
-- assert new_offer.get_requested_amounts() == {None: 700, str_to_tail_hash("red"): 300}
260
-- assert new_offer.is_valid()
261
--
262
-- # Create yet another offer of BLUE for XCH and RED
263
-- blue_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
264
-- None: [
265
-- Payment(acs_ph, 200, [b"blue memo"]),
266
-- ],
267
-- str_to_tail_hash("red"): [
268
-- Payment(acs_ph, 50, [b"blue memo"]),
269
-- ],
270
-- }
271
--
272
-- blue_requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = Offer.notarize_payments(
273
-- blue_requested_payments, blue_coins
274
-- )
275
-- blue_announcements: List[AssertCoinAnnouncement] = Offer.calculate_announcements(
276
-- blue_requested_payments, driver_dict
277
-- )
278
-- blue_secured_bundle: SpendBundle = generate_secure_bundle(
279
-- blue_coins, blue_announcements, 2000, tail_str="blue"
280
-- )
281
-- blue_offer = Offer(blue_requested_payments, blue_secured_bundle, driver_dict)
282
-- assert not blue_offer.is_valid()
283
--
284
-- # Test a re-aggregation
285
-- new_offer: Offer = Offer.aggregate([new_offer, blue_offer])
286
-- assert new_offer.get_offered_amounts() == {
287
-- None: 1000,
288
-- str_to_tail_hash("red"): 350,
289
-- str_to_tail_hash("blue"): 2000,
290
-- }
291
-- assert new_offer.get_requested_amounts() == {None: 900, str_to_tail_hash("red"): 350}
292
-- assert new_offer.summary() == (
293
-- {
294
-- "xch": 1000,
295
-- str_to_tail_hash("red").hex(): 350,
296
-- str_to_tail_hash("blue").hex(): 2000,
297
-- },
298
-- {"xch": 900, str_to_tail_hash("red").hex(): 350},
299
-- driver_dict_as_infos,
300
-- ConditionValidTimes(),
301
-- )
302
-- assert new_offer.get_pending_amounts() == {
303
-- "xch": 1200,
304
-- str_to_tail_hash("red").hex(): 350,
305
-- str_to_tail_hash("blue").hex(): 3000,
306
-- }
307
-- assert new_offer.is_valid()
308
--
309
-- # Test preventing TAIL from running during exchange
310
-- blue_cat_puz: Program = construct_cat_puzzle(CAT_MOD, str_to_tail_hash("blue"), OFFER_MOD)
311
-- blue_spend: CoinSpend = make_spend(
312
-- Coin(bytes32(32), blue_cat_puz.get_tree_hash(), uint64(0)),
313
-- blue_cat_puz,
314
-- Program.to([[bytes32(32), [bytes32(32), 200, ["hey there"]]]]),
315
-- )
316
-- new_spends_list: List[CoinSpend] = [blue_spend, *new_offer.to_spend_bundle().coin_spends]
317
-- tail_offer: Offer = Offer.from_spend_bundle(SpendBundle(new_spends_list, G2Element()))
318
-- valid_spend = tail_offer.to_valid_spend(bytes32(32))
319
-- real_blue_spend = [spend for spend in valid_spend.coin_spends if b"hey there" in bytes(spend)][0]
320
-- real_blue_spend_replaced = replace(
321
-- real_blue_spend,
322
-- solution=SerializedProgram.from_program(
323
-- real_blue_spend.solution.to_program().replace(
324
-- ffrfrf=Program.to(-113), ffrfrr=Program.to([str_to_tail("blue"), []])
325
-- )
326
-- ),
327
-- )
328
-- valid_spend = SpendBundle(
329
-- [real_blue_spend_replaced, *[spend for spend in valid_spend.coin_spends if spend != real_blue_spend]],
330
-- G2Element(),
331
-- )
332
-- with pytest.raises(ValueError, match="clvm raise"):
333
-- valid_spend.additions()
163
++ @pytest.mark.anyio()
164
++ async def test_complex_offer(cost_logger: CostLogger) -> None:
165
++ async with sim_and_client() as (sim, sim_client):
166
++ coins_needed = {None: [500, 400, 300], "red": [250, 100], "blue": [3000]}
167
++ all_coins = await generate_coins(sim, sim_client, coins_needed)
168
++ chia_coins = all_coins[None]
169
++ red_coins = all_coins["red"]
170
++ blue_coins = all_coins["blue"]
171
++
172
++ driver_dict = {
173
++ str_to_tail_hash("red"): PuzzleInfo(
174
++ {"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("red").hex()}
175
++ ),
176
++ str_to_tail_hash("blue"): PuzzleInfo(
177
++ {"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("blue").hex()}
178
++ ),
179
++ }
180
++ driver_dict_as_infos = {key.hex(): value.info for key, value in driver_dict.items()}
181
++
182
++ # Create an XCH Offer for RED
183
++ chia_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
184
++ str_to_tail_hash("red"): [Payment(acs_ph, uint64(100), [b"memo"]), Payment(acs_ph, uint64(200), [b"memo"])]
185
++ }
186
++ chia_notarized_payments = Offer.notarize_payments(chia_requested_payments, chia_coins)
187
++ chia_announcements = Offer.calculate_announcements(chia_notarized_payments, driver_dict)
188
++ chia_secured_bundle = generate_secure_bundle(chia_coins, chia_announcements, uint64(1000))
189
++ chia_offer = Offer(chia_notarized_payments, chia_secured_bundle, driver_dict)
190
++ assert not chia_offer.is_valid()
191
++
192
++ # Create a RED Offer for XCH
193
++ red_coins_1 = red_coins[0:1]
194
++ red_coins_2 = red_coins[1:]
195
++ red_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
196
++ None: [Payment(acs_ph, uint64(300), [b"red memo"]), Payment(acs_ph, uint64(350), [b"red memo"])]
197
++ }
198
++ red_notarized_payments = Offer.notarize_payments(red_requested_payments, red_coins_1)
199
++ red_announcements = Offer.calculate_announcements(red_notarized_payments, driver_dict)
200
++ red_secured_bundle = generate_secure_bundle(
201
++ red_coins_1, red_announcements, uint64(sum([c.amount for c in red_coins_1])), tail_str="red"
202
++ )
203
++ red_offer = Offer(red_notarized_payments, red_secured_bundle, driver_dict)
204
++ assert not red_offer.is_valid()
205
++
206
++ red_requested_payments_2: Dict[Optional[bytes32], List[Payment]] = {
207
++ None: [Payment(acs_ph, uint64(50), [b"red memo"])]
208
++ }
209
++ red_notarized_payments_2 = Offer.notarize_payments(red_requested_payments_2, red_coins_2)
210
++ red_announcements_2 = Offer.calculate_announcements(red_notarized_payments_2, driver_dict)
211
++ red_secured_bundle_2 = generate_secure_bundle(
212
++ red_coins_2, red_announcements_2, uint64(sum([c.amount for c in red_coins_2])), tail_str="red"
213
++ )
214
++ red_offer_2 = Offer(red_notarized_payments_2, red_secured_bundle_2, driver_dict)
215
++ assert not red_offer_2.is_valid()
216
++
217
++ # Test aggregation of offers
218
++ new_offer = Offer.aggregate([chia_offer, red_offer, red_offer_2])
219
++ assert new_offer.get_offered_amounts() == {None: 1000, str_to_tail_hash("red"): 350}
220
++ assert new_offer.get_requested_amounts() == {None: 700, str_to_tail_hash("red"): 300}
221
++ assert new_offer.is_valid()
222
++
223
++ # Create yet another offer of BLUE for XCH and RED
224
++ blue_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
225
++ None: [Payment(acs_ph, uint64(200), [b"blue memo"])],
226
++ str_to_tail_hash("red"): [Payment(acs_ph, uint64(50), [b"blue memo"])],
227
++ }
228
++ blue_notarized_payments = Offer.notarize_payments(blue_requested_payments, blue_coins)
229
++ blue_announcements = Offer.calculate_announcements(blue_notarized_payments, driver_dict)
230
++ blue_secured_bundle = generate_secure_bundle(blue_coins, blue_announcements, uint64(2000), tail_str="blue")
231
++ blue_offer = Offer(blue_notarized_payments, blue_secured_bundle, driver_dict)
232
++ assert not blue_offer.is_valid()
233
++
234
++ # Test a re-aggregation
235
++ new_offer = Offer.aggregate([new_offer, blue_offer])
236
++ assert new_offer.get_offered_amounts() == {
237
++ None: 1000,
238
++ str_to_tail_hash("red"): 350,
239
++ str_to_tail_hash("blue"): 2000,
240
++ }
241
++ assert new_offer.get_requested_amounts() == {None: 900, str_to_tail_hash("red"): 350}
242
++ assert new_offer.summary() == (
243
++ {"xch": 1000, str_to_tail_hash("red").hex(): 350, str_to_tail_hash("blue").hex(): 2000},
244
++ {"xch": 900, str_to_tail_hash("red").hex(): 350},
245
++ driver_dict_as_infos,
246
++ ConditionValidTimes(),
247
++ )
248
++ assert new_offer.get_pending_amounts() == {
249
++ "xch": 1200,
250
++ str_to_tail_hash("red").hex(): 350,
251
++ str_to_tail_hash("blue").hex(): 3000,
252
++ }
253
++ assert new_offer.is_valid()
254
++
255
++ # Test preventing TAIL from running during exchange
256
++ blue_cat_puz = construct_cat_puzzle(CAT_MOD, str_to_tail_hash("blue"), OFFER_MOD)
257
++ random_hash = bytes32([0] * 32)
258
++ blue_spend = make_spend(
259
++ Coin(random_hash, blue_cat_puz.get_tree_hash(), uint64(0)),
260
++ blue_cat_puz,
261
++ Program.to([[random_hash, [random_hash, 200, ["hey there"]]]]),
262
++ )
263
++ new_spends_list = [blue_spend, *new_offer.to_spend_bundle().coin_spends]
264
++ tail_offer = Offer.from_spend_bundle(SpendBundle(new_spends_list, G2Element()))
265
++ valid_spend = tail_offer.to_valid_spend(random_hash)
266
++ real_blue_spend = [spend for spend in valid_spend.coin_spends if b"hey there" in bytes(spend)][0]
267
++ real_blue_spend_replaced = replace(
268
++ real_blue_spend,
269
++ solution=SerializedProgram.from_program(
270
++ real_blue_spend.solution.to_program().replace(
271
++ ffrfrf=Program.to(-113), ffrfrr=Program.to([str_to_tail("blue"), []])
272
++ )
273
++ ),
274
++ )
275
++ valid_spend = SpendBundle(
276
++ [real_blue_spend_replaced, *[spend for spend in valid_spend.coin_spends if spend != real_blue_spend]],
277
++ G2Element(),
278
++ )
279
++ with pytest.raises(ValueError, match="clvm raise"):
280
++ valid_spend.additions()
334
281

335
-- # Test (de)serialization
336
-- assert Offer.from_bytes(bytes(new_offer)) == new_offer
282
++ # Test (de)serialization
283
++ assert Offer.from_bytes(bytes(new_offer)) == new_offer
337
284

338
-- # Test compression
339
-- assert Offer.from_compressed(new_offer.compress()) == new_offer
285
++ # Test compression
286
++ assert Offer.from_compressed(new_offer.compress()) == new_offer
340
287

341
-- # Make sure we can actually spend the offer once it's valid
342
-- arbitrage_ph: bytes32 = Program.to([3, [], [], 1]).get_tree_hash()
343
-- offer_bundle: SpendBundle = new_offer.to_valid_spend(arbitrage_ph)
288
++ # Make sure we can actually spend the offer once it's valid
289
++ arbitrage_ph = Program.to([3, [], [], 1]).get_tree_hash()
290
++ offer_bundle = new_offer.to_valid_spend(arbitrage_ph)
344
291

345
-- result = await sim_client.push_tx(cost_logger.add_cost("Complex Offer", offer_bundle))
346
-- assert result == (MempoolInclusionStatus.SUCCESS, None)
347
-- await sim.farm_block()
292
++ result = await sim_client.push_tx(cost_logger.add_cost("Complex Offer", offer_bundle))
293
++ assert result == (MempoolInclusionStatus.SUCCESS, None)
294
++ await sim.farm_block()
tests/wallet/cat_wallet/test_trades.py CHANGED
@@@ -1814,7 -1814,7 +1814,7 @@@ class TestCATTrades
1814
1814
return wallet_node_taker._tx_messages_in_progress == {}
1815
1815

1816
1816
for _ in range(10):
1817
-- print(await wallet_node_taker._resend_queue())
1817
++ await wallet_node_taker._resend_queue()
1818
1818
await time_out_assert(5, check_wallet_cache_empty, True)
1819
1819
offer_tx_records: List[TransactionRecord] = await wallet_node_maker.wallet_state_manager.tx_store.get_not_sent()
1820
1820
await full_node.process_transaction_records(records=offer_tx_records)
tests/wallet/dao_wallet/test_dao_clvm.py CHANGED
@@@ -224,7 -224,7 +224,7 @@@ def test_proposal() -> None
224
224

225
225
with pytest.raises(ValueError) as e_info:
226
226
conditions_dict_for_solution(full_proposal, repeat_solution_2, INFINITE_COST)
227
-- assert e_info.value.args[0] == "clvm raise"
227
++ assert "clvm raise" in e_info.value.args[0]
228
228

229
229
# Test Launch
230
230
current_yes_votes = 0
@@@ -937,7 -937,7 +937,7 @@@ def test_lockup() -> None
937
937
)
938
938
with pytest.raises(ValueError) as e_info:
939
939
conds = full_lockup_puz.run(revote_solution)
940
-- assert e_info.value.args[0] == "clvm raise"
940
++ assert "clvm raise" in e_info.value.args[0]
941
941

942
942
# Test vote removal
943
943
solution = Program.to(
tests/wallet/did_wallet/test_did.py CHANGED
@@@ -23,6 -23,6 +23,8 @@@ from chia.wallet.singleton import creat
23
23
from chia.wallet.util.address_type import AddressType
24
24
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
25
25
from chia.wallet.util.wallet_types import WalletType
26
++ from chia.wallet.wallet_state_manager import WalletStateManager
27
++ from tests.environments.wallet import WalletStateTransition, WalletTestFramework
26
28
from tests.util.setup_nodes import OldSimulatorsAndWallets
27
29
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
28
30

@@@ -109,9 -109,9 +111,7 @@@ class TestDIDWallet
109
111

110
112
#######################
111
113
all_node_0_wallets = await wallet_node_0.wallet_state_manager.user_store.get_all_wallet_info_entries()
112
-- print(f"Node 0: {all_node_0_wallets}")
113
114
all_node_1_wallets = await wallet_node_1.wallet_state_manager.user_store.get_all_wallet_info_entries()
114
-- print(f"Node 1: {all_node_1_wallets}")
115
115
assert (
116
116
json.loads(all_node_0_wallets[1].data)["current_inner"]
117
117
== json.loads(all_node_1_wallets[1].data)["current_inner"]
@@@ -1268,9 -1268,9 +1268,7 @@@
1268
1268

1269
1269
#######################
1270
1270
all_node_0_wallets = await wallet_node_0.wallet_state_manager.user_store.get_all_wallet_info_entries()
1271
-- print(f"Node 0: {all_node_0_wallets}")
1272
1271
all_node_1_wallets = await wallet_node_1.wallet_state_manager.user_store.get_all_wallet_info_entries()
1273
-- print(f"Node 1: {all_node_1_wallets}")
1274
1272
assert len(all_node_0_wallets) == len(all_node_1_wallets)
1275
1273

1276
1274
# Note that the inner program we expect is different than the on-chain inner.
@@@ -1369,3 -1369,3 +1367,68 @@@
1369
1367
await time_out_assert(30, get_wallet_num, 2, wallet_node_2.wallet_state_manager)
1370
1368
did_wallet_2 = wallet_node_2.wallet_state_manager.get_wallet(uint32(2), DIDWallet)
1371
1369
assert did_info == did_wallet_2.did_info
1370
++
1371
++
1372
++ @pytest.mark.parametrize(
1373
++ "wallet_environments",
1374
++ [
1375
++ {
1376
++ "num_environments": 1,
1377
++ "blocks_needed": [1],
1378
++ }
1379
++ ],
1380
++ indirect=True,
1381
++ )
1382
++ @pytest.mark.anyio
1383
++ async def test_did_coin_records(wallet_environments: WalletTestFramework, monkeypatch: pytest.MonkeyPatch) -> None:
1384
++ # Setup
1385
++ wallet_node = wallet_environments.environments[0].node
1386
++ wallet = wallet_environments.environments[0].xch_wallet
1387
++ client = wallet_environments.environments[0].rpc_client
1388
++
1389
++ # Generate DID wallet
1390
++ did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(wallet_node.wallet_state_manager, wallet, uint64(1))
1391
++
1392
++ await wallet_environments.process_pending_states(
1393
++ [
1394
++ WalletStateTransition(
1395
++ pre_block_balance_updates={
1396
++ 1: {"set_remainder": True},
1397
++ 2: {"init": True, "set_remainder": True},
1398
++ },
1399
++ post_block_balance_updates={
1400
++ 1: {"set_remainder": True},
1401
++ 2: {"set_remainder": True},
1402
++ },
1403
++ ),
1404
++ WalletStateTransition(),
1405
++ ]
1406
++ )
1407
++
1408
++ # When transfer_did doesn't push the transactions automatically, this monkeypatching is no longer necessary
1409
++ with monkeypatch.context() as m:
1410
++
1411
++ async def nothing(*args) -> None:
1412
++ pass
1413
++
1414
++ m.setattr(WalletStateManager, "add_pending_transactions", nothing)
1415
++ for _ in range(0, 2):
1416
++ [tx] = await did_wallet.transfer_did(
1417
++ await wallet.get_puzzle_hash(new=False), uint64(0), True, wallet_environments.tx_config
1418
++ )
1419
++ assert tx.spend_bundle is not None
1420
++ await client.push_tx(tx.spend_bundle)
1421
++ await wallet_environments.process_pending_states(
1422
++ [
1423
++ WalletStateTransition(
1424
++ pre_block_balance_updates={},
1425
++ post_block_balance_updates={
1426
++ 1: {"set_remainder": True},
1427
++ 2: {"set_remainder": True},
1428
++ },
1429
++ ),
1430
++ WalletStateTransition(),
1431
++ ]
1432
++ )
1433
++
1434
++ assert len(await wallet.wallet_state_manager.get_spendable_coins_for_wallet(did_wallet.id())) == 1
tests/wallet/nft_wallet/test_nft_bulk_mint.py CHANGED
@@@ -233,6 -233,6 +233,8 @@@ async def test_nft_mint_from_did_rpc
233
233
hex_did_id = did_wallet_maker.get_my_DID()
234
234
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
235
235

236
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
237
++
236
238
nft_wallet_maker = await api_maker.create_new_wallet(
237
239
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
238
240
)
@@@ -427,6 -427,6 +429,8 @@@ async def test_nft_mint_from_did_rpc_no
427
429
hex_did_id = did_wallet_maker.get_my_DID()
428
430
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
429
431

432
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
433
++
430
434
nft_wallet_maker = await api_maker.create_new_wallet(
431
435
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
432
436
)
@@@ -832,6 -832,6 +836,8 @@@ async def test_nft_mint_from_xch_rpc
832
836
hex_did_id = did_wallet_maker.get_my_DID()
833
837
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
834
838

839
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
840
++
835
841
nft_wallet_maker = await api_maker.create_new_wallet(
836
842
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
837
843
)
tests/wallet/nft_wallet/test_nft_offers.py CHANGED
@@@ -905,3 -905,3 +905,276 @@@ async def test_nft_offer_nft_for_nft
905
905

906
906
assert await nft_wallet_maker.get_nft_count() == 1
907
907
assert await nft_wallet_taker.get_nft_count() == 1
908
++
909
++
910
++ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="save time")
911
++ @pytest.mark.parametrize("trusted", [True, False])
912
++ @pytest.mark.parametrize("reuse_puzhash", [True, False])
913
++ @pytest.mark.anyio
914
++ async def test_nft_offer_nft0_and_xch_for_cat(
915
++ self_hostname: str,
916
++ two_wallet_nodes: Any,
917
++ trusted: Any,
918
++ reuse_puzhash: bool,
919
++ seeded_random: random.Random,
920
++ ) -> None:
921
++ full_nodes, wallets, _ = two_wallet_nodes
922
++ full_node_api: FullNodeSimulator = full_nodes[0]
923
++ full_node_server = full_node_api.server
924
++ wallet_node_0, server_0 = wallets[0]
925
++ wallet_node_1, server_1 = wallets[1]
926
++ wallet_maker = wallet_node_0.wallet_state_manager.main_wallet
927
++ wallet_taker = wallet_node_1.wallet_state_manager.main_wallet
928
++
929
++ maker_ph = await wallet_maker.get_new_puzzlehash()
930
++ taker_ph = await wallet_taker.get_new_puzzlehash()
931
++ token_ph = bytes32.random(seeded_random)
932
++
933
++ if trusted:
934
++ wallet_node_0.config["trusted_peers"] = {
935
++ full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
936
++ }
937
++ wallet_node_1.config["trusted_peers"] = {
938
++ full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
939
++ }
940
++ else:
941
++ wallet_node_0.config["trusted_peers"] = {}
942
++ wallet_node_1.config["trusted_peers"] = {}
943
++
944
++ await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
945
++ await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
946
++
947
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(maker_ph))
948
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(taker_ph))
949
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
950
++
951
++ funds = sum([calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, 2)])
952
++
953
++ await time_out_assert(20, wallet_maker.get_unconfirmed_balance, funds)
954
++ await time_out_assert(20, wallet_maker.get_confirmed_balance, funds)
955
++
956
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
957
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
958
++
959
++ tx_config = DEFAULT_TX_CONFIG.override(reuse_puzhash=reuse_puzhash)
960
++
961
++ # Create NFT wallets and nfts for maker and taker
962
++ nft_wallet_maker = await NFTWallet.create_new_nft_wallet(
963
++ wallet_node_0.wallet_state_manager, wallet_maker, name="NFT WALLET 1"
964
++ )
965
++
966
++ nft_wallet_taker = await NFTWallet.create_new_nft_wallet(
967
++ wallet_node_1.wallet_state_manager, wallet_taker, name="NFT WALLET 2"
968
++ )
969
++
970
++ trade_manager_maker = wallet_maker.wallet_state_manager.trade_manager
971
++ trade_manager_taker = wallet_taker.wallet_state_manager.trade_manager
972
++
973
++ metadata = Program.to(
974
++ [
975
++ ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
976
++ ("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
977
++ ]
978
++ )
979
++
980
++ txs = await nft_wallet_maker.generate_new_nft(metadata, tx_config)
981
++ txs = await wallet_maker.wallet_state_manager.add_pending_transactions(txs)
982
++ for tx in txs:
983
++ if tx.spend_bundle is not None:
984
++ await time_out_assert_not_none(
985
++ 20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
986
++ )
987
++
988
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
989
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
990
++
991
++ coins_maker = await nft_wallet_maker.get_current_nfts()
992
++ assert len(coins_maker) == 1
993
++ assert await nft_wallet_taker.get_nft_count() == 0
994
++ # Create two new CATs and wallets for maker and taker
995
++ cats_to_mint = 10000
996
++ async with wallet_node_0.wallet_state_manager.lock:
997
++ cat_wallet_maker, _ = await CATWallet.create_new_cat_wallet(
998
++ wallet_node_0.wallet_state_manager,
999
++ wallet_maker,
1000
++ {"identifier": "genesis_by_id"},
1001
++ uint64(cats_to_mint),
1002
++ tx_config,
1003
++ )
1004
++ await time_out_assert(20, mempool_not_empty, True, full_node_api)
1005
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
1006
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
1007
++
1008
++ async with wallet_node_1.wallet_state_manager.lock:
1009
++ cat_wallet_taker, _ = await CATWallet.create_new_cat_wallet(
1010
++ wallet_node_1.wallet_state_manager,
1011
++ wallet_taker,
1012
++ {"identifier": "genesis_by_id"},
1013
++ uint64(cats_to_mint),
1014
++ tx_config,
1015
++ )
1016
++ await time_out_assert(20, mempool_not_empty, True, full_node_api)
1017
++
1018
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
1019
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
1020
++
1021
++ await time_out_assert(20, cat_wallet_maker.get_confirmed_balance, cats_to_mint)
1022
++ await time_out_assert(20, cat_wallet_maker.get_unconfirmed_balance, cats_to_mint)
1023
++ await time_out_assert(20, cat_wallet_taker.get_confirmed_balance, cats_to_mint)
1024
++ await time_out_assert(20, cat_wallet_taker.get_unconfirmed_balance, cats_to_mint)
1025
++
1026
++ wallet_maker_for_taker_cat: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
1027
++ wallet_node_0.wallet_state_manager, wallet_maker, cat_wallet_taker.get_asset_id()
1028
++ )
1029
++
1030
++ wallet_taker_for_maker_cat: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
1031
++ wallet_node_1.wallet_state_manager, wallet_taker, cat_wallet_maker.get_asset_id()
1032
++ )
1033
++
1034
++ assert wallet_taker_for_maker_cat
1035
++ # MAKE FIRST TRADE: 1 NFT for 10 taker cats
1036
++ maker_balance_pre = await wallet_maker.get_confirmed_balance()
1037
++ taker_balance_pre = await wallet_taker.get_confirmed_balance()
1038
++ taker_cat_maker_balance_pre = await wallet_maker_for_taker_cat.get_confirmed_balance()
1039
++ taker_cat_taker_balance_pre = await cat_wallet_taker.get_confirmed_balance()
1040
++
1041
++ nft_to_offer = coins_maker[0]
1042
++ nft_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_to_offer.full_puzzle))
1043
++ nft_asset_id: bytes32 = create_asset_id(nft_info) # type: ignore
1044
++ driver_dict: Dict[bytes32, Optional[PuzzleInfo]] = {nft_asset_id: nft_info}
1045
++
1046
++ maker_fee = uint64(10)
1047
++ maker_xch_offered = 1000
1048
++ taker_cat_offered = 2500
1049
++ wallet_maker_id = wallet_maker.id()
1050
++ offer_nft_for_cat = {
1051
++ wallet_maker_id: -maker_xch_offered,
1052
++ nft_asset_id: -1,
1053
++ wallet_maker_for_taker_cat.id(): taker_cat_offered,
1054
++ }
1055
++ maker_unused_index = (
1056
++ await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1057
++ ).index
1058
++ taker_unused_index = (
1059
++ await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1060
++ ).index
1061
++
1062
++ success, trade_make, _, error = await trade_manager_maker.create_offer_for_ids(
1063
++ offer_nft_for_cat, tx_config, driver_dict, fee=maker_fee
1064
++ )
1065
++ assert success is True
1066
++ assert error is None
1067
++ assert trade_make is not None
1068
++
1069
++ taker_fee = uint64(1)
1070
++
1071
++ peer = wallet_node_1.get_full_node_peer()
1072
++ trade_take, tx_records = await trade_manager_taker.respond_to_offer(
1073
++ Offer.from_bytes(trade_make.offer),
1074
++ peer,
1075
++ tx_config,
1076
++ fee=taker_fee,
1077
++ )
1078
++
1079
++ assert trade_take is not None
1080
++ assert tx_records is not None
1081
++
1082
++ tx_records = await trade_manager_taker.wallet_state_manager.add_pending_transactions(tx_records)
1083
++ await full_node_api.process_transaction_records(records=tx_records)
1084
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
1085
++
1086
++ await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_maker, trade_make)
1087
++ await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_taker, trade_take)
1088
++
1089
++ taker_cat_maker_balance_post = await wallet_maker_for_taker_cat.get_confirmed_balance()
1090
++ taker_cat_taker_balance_post = await cat_wallet_taker.get_confirmed_balance()
1091
++ assert taker_cat_maker_balance_post == taker_cat_maker_balance_pre + taker_cat_offered
1092
++ assert taker_cat_taker_balance_post == taker_cat_taker_balance_pre - taker_cat_offered
1093
++ maker_balance_post = await wallet_maker.get_confirmed_balance()
1094
++ taker_balance_post = await wallet_taker.get_confirmed_balance()
1095
++ assert maker_balance_post == maker_balance_pre - maker_fee - maker_xch_offered
1096
++ assert taker_balance_post == taker_balance_pre - taker_fee + maker_xch_offered
1097
++ coins_taker = await nft_wallet_taker.get_current_nfts()
1098
++ assert len(coins_taker) == 1
1099
++
1100
++ assert await nft_wallet_maker.get_nft_count() == 0
1101
++ if reuse_puzhash:
1102
++ # Check if unused index changed
1103
++ assert (
1104
++ maker_unused_index
1105
++ == (
1106
++ await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1107
++ ).index
1108
++ )
1109
++ assert (
1110
++ taker_unused_index
1111
++ == (
1112
++ await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1113
++ ).index
1114
++ )
1115
++ else:
1116
++ assert (
1117
++ maker_unused_index
1118
++ < (
1119
++ await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1120
++ ).index
1121
++ )
1122
++ assert (
1123
++ taker_unused_index
1124
++ < (
1125
++ await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
1126
++ ).index
1127
++ )
1128
++ # Make an offer for taker NFT for multiple cats
1129
++ maker_cat_amount = 400
1130
++ taker_cat_amount = 500
1131
++
1132
++ nft_to_buy = coins_taker[0]
1133
++ nft_to_buy_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_to_buy.full_puzzle))
1134
++ nft_to_buy_asset_id: bytes32 = create_asset_id(nft_to_buy_info) # type: ignore
1135
++
1136
++ driver_dict_to_buy: Dict[bytes32, Optional[PuzzleInfo]] = {
1137
++ nft_to_buy_asset_id: nft_to_buy_info,
1138
++ }
1139
++
1140
++ maker_fee = uint64(10)
1141
++ offer_multi_cats_for_nft = {
1142
++ nft_to_buy_asset_id: 1,
1143
++ wallet_maker_for_taker_cat.id(): -taker_cat_amount,
1144
++ cat_wallet_maker.id(): -maker_cat_amount,
1145
++ }
1146
++
1147
++ success, trade_make, _, error = await trade_manager_maker.create_offer_for_ids(
1148
++ offer_multi_cats_for_nft, tx_config, driver_dict_to_buy, fee=maker_fee
1149
++ )
1150
++ assert success is True
1151
++ assert error is None
1152
++ assert trade_make is not None
1153
++
1154
++ taker_fee = uint64(1)
1155
++
1156
++ trade_take, tx_records = await trade_manager_taker.respond_to_offer(
1157
++ Offer.from_bytes(trade_make.offer), peer, tx_config, fee=taker_fee
1158
++ )
1159
++
1160
++ assert trade_take is not None
1161
++ assert tx_records is not None
1162
++
1163
++ tx_records = await trade_manager_taker.wallet_state_manager.add_pending_transactions(tx_records)
1164
++ await full_node_api.process_transaction_records(records=tx_records)
1165
++ # check balances: taker wallet down an NFT, up cats
1166
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
1167
++
1168
++ await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_maker, trade_make)
1169
++ await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_taker, trade_take)
1170
++
1171
++ taker_cat_maker_balance_post_2 = await wallet_maker_for_taker_cat.get_confirmed_balance()
1172
++ taker_cat_taker_balance_post_2 = await cat_wallet_taker.get_confirmed_balance()
1173
++ assert taker_cat_maker_balance_post_2 == taker_cat_maker_balance_post - taker_cat_amount
1174
++ assert taker_cat_taker_balance_post_2 == taker_cat_taker_balance_post + taker_cat_amount
1175
++ maker_balance_post_2 = await wallet_maker.get_confirmed_balance()
1176
++ taker_balance_post_2 = await wallet_taker.get_confirmed_balance()
1177
++ assert maker_balance_post_2 == maker_balance_post - maker_fee
1178
++ assert taker_balance_post_2 == taker_balance_post - taker_fee
1179
++ assert await nft_wallet_maker.get_nft_count() == 1
1180
++ assert await nft_wallet_taker.get_nft_count() == 0
tests/wallet/nft_wallet/test_nft_wallet.py CHANGED
@@@ -1,6 -1,6 +1,7 @@@
1
1
from __future__ import annotations
2
2

3
3
import asyncio
4
++ import sys
4
5
import time
5
6
from typing import Any, Awaitable, Callable, Dict, List, Optional
6
7

@@@ -22,6 -22,6 +23,7 @@@ from chia.util.byte_types import hexstr
22
23
from chia.util.ints import uint32, uint64
23
24
from chia.util.timing import adjusted_timeout
24
25
from chia.wallet.did_wallet.did_wallet import DIDWallet
26
++ from chia.wallet.nft_wallet.nft_info import NFTInfo
25
27
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
26
28
from chia.wallet.transaction_record import TransactionRecord
27
29
from chia.wallet.util.address_type import AddressType
@@@ -31,6 -31,6 +33,7 @@@ from chia.wallet.util.wallet_types impo
31
33
from chia.wallet.wallet_node import WalletNode
32
34
from chia.wallet.wallet_state_manager import WalletStateManager
33
35
from tests.conftest import ConsensusMode
36
++ from tests.util.setup_nodes import OldSimulatorsAndWallets
34
37
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
35
38

36
39

@@@ -44,10 -44,10 +47,10 @@@ async def get_wallet_number(manager: Wa
44
47

45
48
async def wait_rpc_state_condition(
46
49
timeout: float,
47
-- async_function: Callable[[Dict[str, Any]], Awaitable[Dict]],
48
-- params: List[Dict],
50
++ async_function: Callable[[Dict[str, Any]], Awaitable[Dict[str, Any]]],
51
++ params: List[Dict[str, Any]],
49
52
condition_func: Callable[[Dict[str, Any]], bool],
50
-- ) -> Dict:
53
++ ) -> Dict[str, Any]:
51
54
__tracebackhide__ = True
52
55

53
56
timeout = adjusted_timeout(timeout=timeout)
@@@ -71,7 -71,7 +74,7 @@@
71
74

72
75

73
76
async def make_new_block_with(
74
-- resp: Dict, full_node_api: FullNodeSimulator, ph: bytes32, node_to_sync: Optional[WalletNode] = None
77
++ resp: Dict[str, Any], full_node_api: FullNodeSimulator, ph: bytes32, node_to_sync: Optional[WalletNode] = None
75
78
) -> SpendBundle:
76
79
assert resp.get("success")
77
80
sb = resp["spend_bundle"]
@@@ -83,12 -83,12 +86,11 @@@
83
86
return sb
84
87

85
88

86
-- @pytest.mark.parametrize(
87
-- "trusted",
88
-- [True, False],
89
-- )
89
++ @pytest.mark.parametrize("trusted", [True, False])
90
90
@pytest.mark.anyio
91
-- async def test_nft_wallet_creation_automatically(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
91
++ async def test_nft_wallet_creation_automatically(
92
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
93
++ ) -> None:
92
94
num_blocks = 3
93
95
full_nodes, wallets, _ = two_wallet_nodes
94
96
full_node_api = full_nodes[0]
@@@ -115,7 -115,7 +117,7 @@@
115
117
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
116
118
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
117
119

118
-- for i in range(1, num_blocks):
120
++ for _ in range(1, num_blocks):
119
121
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
120
122

121
123
funds = sum(
@@@ -124,10 -124,10 +126,10 @@@
124
126

125
127
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
126
128
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
127
-- for i in range(1, num_blocks):
129
++ for _ in range(1, num_blocks):
128
130
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
129
131

130
-- for i in range(1, num_blocks):
132
++ for _ in range(1, num_blocks):
131
133
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
132
134

133
135
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
@@@ -135,21 -135,21 +137,18 @@@
135
137
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
136
138
)
137
139
metadata = Program.to(
138
-- [
139
-- ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
140
-- ("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
141
-- ]
140
++ [("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
142
141
)
143
142

144
143
txs = await nft_wallet_0.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
145
-- await nft_wallet_0.wallet_state_manager.add_pending_transactions(txs)
144
++ txs = await nft_wallet_0.wallet_state_manager.add_pending_transactions(txs)
146
145
for tx in txs:
147
146
if tx.spend_bundle is not None:
148
147
await time_out_assert_not_none(
149
148
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
150
149
)
151
150

152
-- for i in range(1, num_blocks):
151
++ for _ in range(1, num_blocks):
153
152
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
154
153

155
154
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
@@@ -165,7 -165,7 +164,7 @@@
165
164
await time_out_assert_not_none(
166
165
30, full_node_api.full_node.mempool_manager.get_spendbundle, txs[0].spend_bundle.name()
167
166
)
168
-- for i in range(1, num_blocks):
167
++ for _ in range(1, num_blocks):
169
168
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
170
169

171
170
async def num_wallets() -> int:
@@@ -175,7 -175,7 +174,8 @@@
175
174
# Get the new NFT wallet
176
175
nft_wallets = await wallet_node_1.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
177
176
assert len(nft_wallets) == 1
178
-- nft_wallet_1: NFTWallet = wallet_node_1.wallet_state_manager.wallets[nft_wallets[0].id]
177
++ nft_wallet_1 = wallet_node_1.wallet_state_manager.wallets[nft_wallets[0].id]
178
++ assert isinstance(nft_wallet_1, NFTWallet)
179
179
await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
180
180
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
181
181

@@@ -183,13 -183,13 +183,16 @@@
183
183
assert await nft_wallet_1.get_nft_count() == 1
184
184

185
185

186
++ @pytest.mark.skipif(sys.platform == "win32" and sys.version_info < (3, 9), reason="Flaky on Windows+3.8")
186
187
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
187
188
@pytest.mark.parametrize("trusted", [True, False])
188
189
@pytest.mark.anyio
189
-- async def test_nft_wallet_creation_and_transfer(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
190
++ async def test_nft_wallet_creation_and_transfer(
191
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
192
++ ) -> None:
190
193
num_blocks = 2
191
194
full_nodes, wallets, _ = two_wallet_nodes
192
-- full_node_api: FullNodeSimulator = full_nodes[0]
195
++ full_node_api = full_nodes[0]
193
196
full_node_server = full_node_api.server
194
197
wallet_node_0, server_0 = wallets[0]
195
198
wallet_node_1, server_1 = wallets[1]
@@@ -198,6 -198,6 +201,10 @@@
198
201
ph = await wallet_0.get_new_puzzlehash()
199
202
ph1 = await wallet_1.get_new_puzzlehash()
200
203

204
++ async def ensure_wallet_sync() -> None:
205
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
206
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_1, timeout=20)
207
++
201
208
if trusted:
202
209
wallet_node_0.config["trusted_peers"] = {
203
210
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
@@@ -212,7 -212,7 +219,7 @@@
212
219
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
213
220
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
214
221

215
-- for i in range(1, num_blocks):
222
++ for _ in range(1, num_blocks):
216
223
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
217
224

218
225
funds = sum(
@@@ -221,10 -221,10 +228,10 @@@
221
228

222
229
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
223
230
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
224
-- for i in range(1, num_blocks):
231
++ for _ in range(1, num_blocks):
225
232
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
226
233

227
-- for i in range(1, num_blocks):
234
++ for _ in range(1, num_blocks):
228
235
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
229
236

230
237
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
@@@ -232,10 -232,10 +239,7 @@@
232
239
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
233
240
)
234
241
metadata = Program.to(
235
-- [
236
-- ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
237
-- ("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
238
-- ]
242
++ [("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
239
243
)
240
244

241
245
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 2000000000000)
@@@ -249,7 -249,7 +253,7 @@@
249
253
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
250
254
)
251
255

252
-- for i in range(1, num_blocks):
256
++ for _ in range(1, num_blocks):
253
257
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
254
258

255
259
await time_out_assert(10, get_nft_count, 1, nft_wallet_0)
@@@ -258,7 -258,7 +262,11 @@@
258
262
# Test Reorg mint
259
263
height = full_node_api.full_node.blockchain.get_peak_height()
260
264
assert height is not None
265
++
261
266
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(height - 1), uint32(height + 1), ph1, None))
267
++
268
++ await time_out_assert(60, full_node_api.full_node.blockchain.get_peak_height, height + 1)
269
++
262
270
await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
263
271
await time_out_assert(30, get_wallet_number, 2, wallet_node_0.wallet_state_manager)
264
272

@@@ -266,12 -266,12 +274,7 @@@
266
274
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
267
275
)
268
276

269
-- metadata = Program.to(
270
-- [
271
-- ("u", ["https://www.test.net/logo.svg"]),
272
-- ("h", "0xD4584AD463139FA8C0D9F68F4B59F181"),
273
-- ]
274
-- )
277
++ metadata = Program.to([("u", ["https://www.test.net/logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F181")])
275
278

276
279
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 4000000000000 - 1)
277
280
await time_out_assert(10, wallet_0.get_confirmed_balance, 4000000000000)
@@@ -286,8 -286,8 +289,9 @@@
286
289
)
287
290

288
291
await time_out_assert(30, wallet_node_0.wallet_state_manager.lock.locked, False)
289
-- for i in range(1, num_blocks * 2):
292
++ for _ in range(1, num_blocks * 2):
290
293
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
294
++
291
295
await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
292
296
coins = await nft_wallet_0.get_current_nfts()
293
297
assert len(coins) == 2, "nft not generated"
@@@ -307,8 -307,8 +311,11 @@@
307
311
)
308
312
assert len(compute_memos(txs[0].spend_bundle)) > 0
309
313

310
-- for i in range(1, num_blocks * 2):
314
++ for _ in range(1, num_blocks * 2):
311
315
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
316
++
317
++ await ensure_wallet_sync()
318
++
312
319
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
313
320
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
314
321

@@@ -329,27 -329,27 +336,37 @@@
329
336
)
330
337
assert len(compute_memos(txs[0].spend_bundle)) > 0
331
338

332
-- for i in range(1, num_blocks):
339
++ await ensure_wallet_sync()
340
++
341
++ # We wish for the wallet to be in a specific state at this point after having
342
++ # become synced above.
343
++ for _ in range(1, num_blocks):
333
344
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
334
345

335
-- await time_out_assert(30, wallet_node_0.wallet_state_manager.lock.locked, False)
346
++ await ensure_wallet_sync()
347
++
348
++ # The wallets are locked so we still observe the conditions above.
336
349
await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
337
350
await time_out_assert(30, get_nft_count, 0, nft_wallet_1)
338
351

339
352
# Test Reorg
340
353
height = full_node_api.full_node.blockchain.get_peak_height()
354
++
341
355
assert height is not None
342
356
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(height - 1), uint32(height + 2), ph1, None))
357
++
358
++ # Wait for wallet sync on all wallets.
359
++ await ensure_wallet_sync()
360
++
343
361
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
344
362
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
345
363

346
364

347
-- @pytest.mark.parametrize(
348
-- "trusted",
349
-- [True, False],
350
-- )
365
++ @pytest.mark.parametrize("trusted", [True, False])
351
366
@pytest.mark.anyio
352
-- async def test_nft_wallet_rpc_creation_and_list(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
367
++ async def test_nft_wallet_rpc_creation_and_list(
368
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
369
++ ) -> None:
353
370
num_blocks = 3
354
371
full_nodes, wallets, _ = two_wallet_nodes
355
372
full_node_api = full_nodes[0]
@@@ -376,7 -376,7 +393,7 @@@
376
393
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
377
394
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
378
395

379
-- for i in range(1, num_blocks):
396
++ for _ in range(1, num_blocks):
380
397
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
381
398
funds = sum(
382
399
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)]
@@@ -407,7 -407,7 +424,7 @@@
407
424
sb = tr1["spend_bundle"]
408
425

409
426
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
410
-- for i in range(1, num_blocks):
427
++ for _ in range(1, num_blocks):
411
428
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
412
429

413
430
await wait_rpc_state_condition(30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"])
@@@ -427,15 -427,15 +444,12 @@@
427
444
assert tr2.get("success")
428
445
sb = tr2["spend_bundle"]
429
446
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
430
-- for i in range(1, num_blocks):
447
++ for _ in range(1, num_blocks):
431
448
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
432
449
coins_response = await wait_rpc_state_condition(
433
-- 5,
434
-- api_0.nft_get_nfts,
435
-- [{"wallet_id": nft_wallet_0_id}],
436
-- lambda x: x["success"] and len(x["nft_list"]) == 2,
450
++ 5, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: x["success"] and len(x["nft_list"]) == 2
437
451
)
438
-- coins = coins_response["nft_list"]
452
++ coins: List[NFTInfo] = coins_response["nft_list"]
439
453
uris = []
440
454
for coin in coins:
441
455
assert not coin.supports_did
@@@ -469,12 -469,12 +483,11 @@@
469
483
assert resp.get("count") is None
470
484

471
485

472
-- @pytest.mark.parametrize(
473
-- "trusted",
474
-- [True, False],
475
-- )
486
++ @pytest.mark.parametrize("trusted", [True, False])
476
487
@pytest.mark.anyio
477
-- async def test_nft_wallet_rpc_update_metadata(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
488
++ async def test_nft_wallet_rpc_update_metadata(
489
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
490
++ ) -> None:
478
491
num_blocks = 3
479
492
full_nodes, wallets, _ = two_wallet_nodes
480
493
full_node_api = full_nodes[0]
@@@ -498,7 -498,7 +511,7 @@@
498
511
wallet_node_0.config["trusted_peers"] = {}
499
512
wallet_node_1.config["trusted_peers"] = {}
500
513

501
-- for i in range(1, num_blocks):
514
++ for _ in range(1, num_blocks):
502
515
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
503
516

504
517
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
@@@ -535,14 -535,14 +548,14 @@@
535
548
sb = resp["spend_bundle"]
536
549

537
550
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
538
-- for i in range(1, num_blocks):
551
++ for _ in range(1, num_blocks):
539
552
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
540
553
coins_response = await wait_rpc_state_condition(
541
554
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
542
555
)
543
556
assert coins_response["nft_list"], isinstance(coins_response, dict)
544
557
assert coins_response.get("success")
545
-- coins = coins_response["nft_list"]
558
++ coins: List[NFTInfo] = coins_response["nft_list"]
546
559
coin = coins[0].to_json_dict()
547
560
assert coin["mint_height"] > 0
548
561
assert coin["data_hash"] == "0xd4584ad463139fa8c0d9f68f4b59f185"
@@@ -567,13 -567,13 +580,14 @@@
567
580
{"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://metadata", "key": "mu"}
568
581
)
569
582

570
-- assert isinstance(tr1, dict)
571
583
assert tr1.get("success")
572
584
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
573
-- assert coins_response["nft_list"][0].pending_transaction
585
++ coins = coins_response["nft_list"]
586
++ assert coins[0].pending_transaction
574
587
sb = tr1["spend_bundle"]
588
++ assert isinstance(sb, SpendBundle)
575
589
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
576
-- for i in range(1, num_blocks):
590
++ for _ in range(1, num_blocks):
577
591
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
578
592
# check that new URI was added
579
593
coins_response = await wait_rpc_state_condition(
@@@ -597,19 -597,19 +611,14 @@@
597
611
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
598
612
nft_coin_id = coin["nft_coin_id"]
599
613
tr1 = await api_0.nft_add_uri(
600
-- {
601
-- "wallet_id": nft_wallet_0_id,
602
-- "nft_coin_id": nft_coin_id,
603
-- "uri": "http://data",
604
-- "key": "u",
605
-- }
614
++ {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://data", "key": "u"}
606
615
)
607
616

608
617
assert isinstance(tr1, dict)
609
618
assert tr1.get("success")
610
619
sb = tr1["spend_bundle"]
611
620
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
612
-- for i in range(1, num_blocks):
621
++ for _ in range(1, num_blocks):
613
622
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
614
623
coins_response = await wait_rpc_state_condition(
615
624
5,
@@@ -627,15 -627,15 +636,14 @@@
627
636
assert "http://data" == coin["data_uris"][0]
628
637

629
638

630
-- @pytest.mark.parametrize(
631
-- "trusted",
632
-- [True, False],
633
-- )
639
++ @pytest.mark.parametrize("trusted", [True, False])
634
640
@pytest.mark.anyio
635
-- async def test_nft_with_did_wallet_creation(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
641
++ async def test_nft_with_did_wallet_creation(
642
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
643
++ ) -> None:
636
644
num_blocks = 3
637
645
full_nodes, wallets, _ = two_wallet_nodes
638
-- full_node_api: FullNodeSimulator = full_nodes[0]
646
++ full_node_api = full_nodes[0]
639
647
full_node_server = full_node_api.server
640
648
wallet_node_0, server_0 = wallets[0]
641
649
wallet_node_1, server_1 = wallets[1]
@@@ -666,12 -666,12 +674,10 @@@
666
674

667
675
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
668
676
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
669
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
670
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
671
-- )
677
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
672
678
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
673
--
674
679
spend_bundle = spend_bundle_list[0].spend_bundle
680
++ assert spend_bundle is not None
675
681
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
676
682

677
683
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
@@@ -715,7 -715,7 +721,7 @@@
715
721
assert res.get("did_id") == hmr_did_id
716
722

717
723
# Create a NFT with DID
718
-- nft_ph: bytes32 = await wallet_0.get_new_puzzlehash()
724
++ nft_ph = await wallet_0.get_new_puzzlehash()
719
725
resp = await api_0.nft_mint_nft(
720
726
{
721
727
"wallet_id": nft_wallet_0_id,
@@@ -768,7 -768,7 +774,7 @@@
768
774
coins_response = await wait_rpc_state_condition(
769
775
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
770
776
)
771
-- coins = coins_response["nft_list"]
777
++ coins: List[NFTInfo] = coins_response["nft_list"]
772
778
assert len(coins) == 1
773
779
did_nft = coins[0].to_json_dict()
774
780
assert did_nft["mint_height"] > 0
@@@ -794,15 -794,15 +800,12 @@@
794
800
assert non_did_nft["owner_did"] is None
795
801

796
802

797
-- @pytest.mark.parametrize(
798
-- "trusted",
799
-- [True, False],
800
-- )
803
++ @pytest.mark.parametrize("trusted", [True, False])
801
804
@pytest.mark.anyio
802
-- async def test_nft_rpc_mint(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
805
++ async def test_nft_rpc_mint(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
803
806
num_blocks = 3
804
807
full_nodes, wallets, _ = two_wallet_nodes
805
-- full_node_api: FullNodeSimulator = full_nodes[0]
808
++ full_node_api = full_nodes[0]
806
809
full_node_server = full_node_api.server
807
810
wallet_node_0, server_0 = wallets[0]
808
811
wallet_node_1, server_1 = wallets[1]
@@@ -835,12 -835,12 +838,10 @@@
835
838

836
839
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
837
840
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
838
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
839
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
840
-- )
841
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
841
842
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
842
--
843
843
spend_bundle = spend_bundle_list[0].spend_bundle
844
++ assert spend_bundle is not None
844
845
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
845
846

846
847
for _ in range(1, num_blocks):
@@@ -884,21 -884,21 +885,21 @@@
884
885
}
885
886
)
886
887
assert resp.get("success")
887
-- nft_id: str = str(resp.get("nft_id"))
888
++ nft_id = str(resp.get("nft_id"))
888
889
sb = resp["spend_bundle"]
889
890

890
891
# ensure hints are generated
891
892
assert len(compute_memos(sb)) > 0
892
893
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
893
894

894
-- for i in range(1, num_blocks):
895
++ for _ in range(1, num_blocks):
895
896
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
896
897
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 9999999999998)
897
898
await time_out_assert(30, wallet_0.get_confirmed_balance, 9999999999998)
898
899
coins_response = await wait_rpc_state_condition(
899
900
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
900
901
)
901
-- coins = coins_response["nft_list"]
902
++ coins: List[NFTInfo] = coins_response["nft_list"]
902
903
assert len(coins) == 1
903
904
did_nft = coins[0]
904
905
assert did_nft.royalty_puzzle_hash == royalty_address
@@@ -913,16 -913,16 +914,15 @@@
913
914
assert decode_puzzle_hash(nft_id) == did_nft.launcher_id
914
915

915
916

916
-- @pytest.mark.parametrize(
917
-- "trusted",
918
-- [True, False],
919
-- )
917
++ @pytest.mark.parametrize("trusted", [True, False])
920
918
@pytest.mark.anyio
921
-- async def test_nft_transfer_nft_with_did(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
919
++ async def test_nft_transfer_nft_with_did(
920
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
921
++ ) -> None:
922
922
num_blocks = 3
923
923
fee = 100
924
924
full_nodes, wallets, _ = two_wallet_nodes
925
-- full_node_api: FullNodeSimulator = full_nodes[0]
925
++ full_node_api = full_nodes[0]
926
926
full_node_server = full_node_api.server
927
927
wallet_node_0, server_0 = wallets[0]
928
928
wallet_node_1, server_1 = wallets[1]
@@@ -958,12 -958,12 +958,10 @@@
958
958
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
959
959
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
960
960
# Create DID
961
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
962
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
963
-- )
961
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
964
962
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
965
--
966
963
spend_bundle = spend_bundle_list[0].spend_bundle
964
++ assert spend_bundle is not None
967
965
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
968
966

969
967
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
@@@ -1000,8 -1000,8 +998,9 @@@
1000
998
)
1001
999
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 5999999999898)
1002
1000
await time_out_assert(30, wallet_0.get_confirmed_balance, 5999999999898)
1003
-- coins = coins_response["nft_list"]
1001
++ coins: List[NFTInfo] = coins_response["nft_list"]
1004
1002
assert len(coins) == 1
1003
++ assert coins[0].owner_did is not None
1005
1004
assert coins[0].owner_did.hex() == hex_did_id
1006
1005

1007
1006
assert len(wallet_1.wallet_state_manager.wallets) == 1, "NFT wallet shouldn't exist yet"
@@@ -1010,13 -1010,13 +1009,13 @@@
1010
1009
await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
1011
1010
# transfer DID to the other wallet
1012
1011
txs = await did_wallet.transfer_did(ph1, uint64(0), True, DEFAULT_TX_CONFIG)
1012
++ txs = await did_wallet.wallet_state_manager.add_pending_transactions(txs)
1013
1013
for tx in txs:
1014
-- await did_wallet.wallet_state_manager.add_pending_transactions(txs)
1015
1014
if tx.spend_bundle is not None:
1016
1015
await time_out_assert_not_none(
1017
1016
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
1018
1017
)
1019
-- for i in range(1, num_blocks):
1018
++ for _ in range(1, num_blocks):
1020
1019
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
1021
1020
await full_node_api.wait_for_wallet_synced(wallet_node_0, 20)
1022
1021
await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
@@@ -1041,7 -1041,7 +1040,7 @@@
1041
1040

1042
1041
# wait for all wallets to be created
1043
1042
await time_out_assert(30, len, 3, wallet_1.wallet_state_manager.wallets)
1044
-- did_wallet_1 = wallet_1.wallet_state_manager.wallets[2]
1043
++ did_wallet_1 = wallet_1.wallet_state_manager.wallets[uint32(2)]
1045
1044
assert nft_wallet_0_id not in wallet_node_0.wallet_state_manager.wallets.keys()
1046
1045
# Check if the NFT owner DID is reset
1047
1046
resp = await api_1.nft_get_by_did({})
@@@ -1081,18 -1081,18 +1080,18 @@@
1081
1080
)
1082
1081
coins = resp["nft_list"]
1083
1082
assert len(coins) == 1
1083
++ assert coins[0].owner_did is not None
1084
1084
assert coins[0].owner_did.hex() == hex_did_id
1085
1085

1086
1086

1087
-- @pytest.mark.parametrize(
1088
-- "trusted",
1089
-- [True, False],
1090
-- )
1087
++ @pytest.mark.parametrize("trusted", [True, False])
1091
1088
@pytest.mark.anyio
1092
-- async def test_update_metadata_for_nft_did(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
1089
++ async def test_update_metadata_for_nft_did(
1090
++ self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
1091
++ ) -> None:
1093
1092
num_blocks = 3
1094
1093
full_nodes, wallets, _ = two_wallet_nodes
1095
-- full_node_api: FullNodeSimulator = full_nodes[0]
1094
++ full_node_api = full_nodes[0]
1096
1095
full_node_server = full_node_api.server
1097
1096
wallet_node_0, server_0 = wallets[0]
1098
1097
wallet_node_1, server_1 = wallets[1]
@@@ -1125,12 -1125,12 +1124,11 @@@
1125
1124

1126
1125
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
1127
1126
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
1128
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
1129
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
1130
-- )
1127
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
1131
1128
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
1132
1129

1133
1130
spend_bundle = spend_bundle_list[0].spend_bundle
1131
++ assert spend_bundle is not None
1134
1132
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
1135
1133

1136
1134
for _ in range(1, num_blocks):
@@@ -1165,7 -1165,7 +1163,7 @@@
1165
1163
assert len(compute_memos(sb)) > 0
1166
1164
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
1167
1165

1168
-- for i in range(1, num_blocks):
1166
++ for _ in range(1, num_blocks):
1169
1167
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1170
1168

1171
1169
# Check DID NFT
@@@ -1173,8 -1173,8 +1171,9 @@@
1173
1171
coins_response = await wait_rpc_state_condition(
1174
1172
30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
1175
1173
)
1176
-- coins = coins_response["nft_list"]
1174
++ coins: List[NFTInfo] = coins_response["nft_list"]
1177
1175
assert len(coins) == 1
1176
++ assert coins[0].minter_did is not None
1178
1177
assert coins[0].minter_did.hex() == hex_did_id
1179
1178
nft_coin_id = coins[0].nft_coin_id
1180
1179

@@@ -1195,7 -1195,7 +1194,7 @@@
1195
1194

1196
1195
sb = tr1["spend_bundle"]
1197
1196
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
1198
-- for i in range(1, num_blocks):
1197
++ for _ in range(1, num_blocks):
1199
1198
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
1200
1199
# check that new URI was added
1201
1200
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 11999999999898)
@@@ -1218,15 -1218,15 +1217,12 @@@
1218
1217
assert len(coin["license_uris"]) == 0
1219
1218

1220
1219

1221
-- @pytest.mark.parametrize(
1222
-- "trusted",
1223
-- [True, False],
1224
-- )
1220
++ @pytest.mark.parametrize("trusted", [True, False])
1225
1221
@pytest.mark.anyio
1226
-- async def test_nft_bulk_set_did(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
1222
++ async def test_nft_bulk_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
1227
1223
num_blocks = 2
1228
1224
full_nodes, wallets, _ = two_wallet_nodes
1229
-- full_node_api: FullNodeSimulator = full_nodes[0]
1225
++ full_node_api = full_nodes[0]
1230
1226
full_node_server = full_node_api.server
1231
1227
wallet_node_0, server_0 = wallets[0]
1232
1228
wallet_node_1, server_1 = wallets[1]
@@@ -1257,11 -1257,11 +1253,10 @@@
1257
1253

1258
1254
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
1259
1255
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
1260
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
1261
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
1262
-- )
1256
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
1263
1257
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
1264
1258
spend_bundle = spend_bundle_list[0].spend_bundle
1259
++ assert spend_bundle is not None
1265
1260
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
1266
1261

1267
1262
for _ in range(1, num_blocks):
@@@ -1274,13 -1274,13 +1269,13 @@@
1274
1269
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)
1275
1270

1276
1271
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
1277
-- assert isinstance(res, dict)
1278
1272
assert res.get("success")
1279
1273
nft_wallet_0_id = res["wallet_id"]
1274
++ assert isinstance(nft_wallet_0_id, uint32)
1280
1275
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 2"))
1281
-- assert isinstance(res, dict)
1282
1276
assert res.get("success")
1283
1277
nft_wallet_1_id = res["wallet_id"]
1278
++ assert isinstance(nft_wallet_1_id, uint32)
1284
1279
await time_out_assert(30, did_wallet.get_confirmed_balance, 1)
1285
1280

1286
1281
# Create a NFT with DID
@@@ -1332,7 -1332,7 +1327,7 @@@
1332
1327
coins_response = await wait_rpc_state_condition(
1333
1328
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
1334
1329
)
1335
-- coins = coins_response["nft_list"]
1330
++ coins: List[NFTInfo] = coins_response["nft_list"]
1336
1331
nft1 = coins[0]
1337
1332
nft12 = coins[1]
1338
1333
assert len(coins) == 2
@@@ -1353,8 -1353,8 +1348,12 @@@
1353
1348
{"nft_coin_id": nft2.nft_coin_id.hex()},
1354
1349
]
1355
1350
resp = await api_0.nft_set_did_bulk(dict(did_id=hmr_did_id, nft_coin_list=nft_coin_list, fee=1000))
1356
-- assert len(resp["spend_bundle"].coin_spends) == 5
1357
-- assert resp["tx_num"] == 4
1351
++ sb = resp["spend_bundle"]
1352
++ assert isinstance(sb, SpendBundle)
1353
++ assert len(sb.coin_spends) == 5
1354
++ tx_num = resp["tx_num"]
1355
++ assert isinstance(tx_num, int)
1356
++ assert tx_num == 4
1358
1357
coins_response = await wait_rpc_state_condition(
1359
1358
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
1360
1359
)
@@@ -1373,23 -1373,23 +1372,25 @@@
1373
1372
[dict(wallet_id=nft_wallet_1_id)],
1374
1373
lambda x: len(x["nft_list"]) > 2 and x["nft_list"][0].owner_did,
1375
1374
)
1376
-- assert await wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id].get_nft_count() == 3
1375
++ nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
1376
++ assert isinstance(nft_wallet_to_check, NFTWallet)
1377
++ assert await nft_wallet_to_check.get_nft_count() == 3
1377
1378
coins = resp["nft_list"]
1378
1379
assert len(coins) == 3
1380
++ assert coins[0].owner_did is not None
1379
1381
assert coins[0].owner_did.hex() == hex_did_id
1382
++ assert coins[1].owner_did is not None
1380
1383
assert coins[1].owner_did.hex() == hex_did_id
1384
++ assert coins[2].owner_did is not None
1381
1385
assert coins[2].owner_did.hex() == hex_did_id
1382
1386

1383
1387

1384
-- @pytest.mark.parametrize(
1385
-- "trusted",
1386
-- [True, False],
1387
-- )
1388
++ @pytest.mark.parametrize("trusted", [True, False])
1388
1389
@pytest.mark.anyio
1389
-- async def test_nft_bulk_transfer(two_wallet_nodes: Any, trusted: Any) -> None:
1390
++ async def test_nft_bulk_transfer(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
1390
1391
num_blocks = 2
1391
1392
full_nodes, wallets, _ = two_wallet_nodes
1392
-- full_node_api: FullNodeSimulator = full_nodes[0]
1393
++ full_node_api = full_nodes[0]
1393
1394
full_node_server = full_node_api.server
1394
1395
wallet_node_0, server_0 = wallets[0]
1395
1396
wallet_node_1, server_1 = wallets[1]
@@@ -1411,8 -1411,8 +1412,8 @@@
1411
1412
wallet_node_0.config["trusted_peers"] = {}
1412
1413
wallet_node_1.config["trusted_peers"] = {}
1413
1414

1414
-- await server_0.start_client(PeerInfo("127.0.0.1", full_node_server.get_port()), None)
1415
-- await server_1.start_client(PeerInfo("127.0.0.1", full_node_server.get_port()), None)
1415
++ await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1416
++ await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1416
1417

1417
1418
for _ in range(1, num_blocks + 1):
1418
1419
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
@@@ -1423,11 -1423,11 +1424,10 @@@
1423
1424

1424
1425
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
1425
1426
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
1426
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
1427
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
1428
-- )
1427
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
1429
1428
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
1430
1429
spend_bundle = spend_bundle_list[0].spend_bundle
1430
++ assert spend_bundle is not None
1431
1431
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
1432
1432

1433
1433
for _ in range(1, num_blocks):
@@@ -1496,7 -1496,7 +1496,7 @@@
1496
1496
coins_response = await wait_rpc_state_condition(
1497
1497
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
1498
1498
)
1499
-- coins = coins_response["nft_list"]
1499
++ coins: List[NFTInfo] = coins_response["nft_list"]
1500
1500
nft1 = coins[0]
1501
1501
nft12 = coins[1]
1502
1502
assert len(coins) == 2
@@@ -1539,10 -1539,10 +1539,10 @@@
1539
1539
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
1540
1540
@pytest.mark.parametrize("trusted", [True, False])
1541
1541
@pytest.mark.anyio
1542
-- async def test_nft_set_did(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
1542
++ async def test_nft_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
1543
1543
num_blocks = 3
1544
1544
full_nodes, wallets, _ = two_wallet_nodes
1545
-- full_node_api: FullNodeSimulator = full_nodes[0]
1545
++ full_node_api = full_nodes[0]
1546
1546
full_node_server = full_node_api.server
1547
1547
wallet_node_0, server_0 = wallets[0]
1548
1548
wallet_node_1, server_1 = wallets[1]
@@@ -1573,11 -1573,11 +1573,10 @@@
1573
1573

1574
1574
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
1575
1575
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
1576
-- did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
1577
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
1578
-- )
1576
++ did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
1579
1577
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
1580
1578
spend_bundle = spend_bundle_list[0].spend_bundle
1579
++ assert spend_bundle is not None
1581
1580
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
1582
1581

1583
1582
for _ in range(1, num_blocks):
@@@ -1613,18 -1613,18 +1612,17 @@@
1613
1612
coins_response = await wait_rpc_state_condition(
1614
1613
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
1615
1614
)
1616
-- coins = coins_response["nft_list"]
1615
++ coins: List[NFTInfo] = coins_response["nft_list"]
1617
1616
assert len(coins) == 1
1618
1617
assert coins[0].owner_did is None
1619
1618
nft_coin_id = coins[0].nft_coin_id
1620
1619

1621
1620
# Test set None -> DID1
1622
-- did_wallet1: DIDWallet = await DIDWallet.create_new_did_wallet(
1623
-- wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
1624
-- )
1621
++ did_wallet1 = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
1625
1622
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet1.id())
1626
1623

1627
1624
spend_bundle = spend_bundle_list[0].spend_bundle
1625
++ assert spend_bundle is not None
1628
1626
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
1629
1627

1630
1628
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
@@@ -1647,10 -1647,10 +1645,13 @@@
1647
1645
[dict(wallet_id=nft_wallet_1_id)],
1648
1646
lambda x: len(x["nft_list"]) > 0 and x["nft_list"][0].owner_did,
1649
1647
)
1650
-- assert len(await wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id].get_current_nfts()) == 0
1648
++ nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
1649
++ assert isinstance(nft_wallet_to_check, NFTWallet)
1650
++ assert len(await nft_wallet_to_check.get_current_nfts()) == 0
1651
1651

1652
1652
coins = resp["nft_list"]
1653
1653
assert len(coins) == 1
1654
++ assert coins[0].owner_did is not None
1654
1655
assert coins[0].owner_did.hex() == hex_did_id
1655
1656
nft_coin_id = coins[0].nft_coin_id
1656
1657

@@@ -1682,6 -1682,6 +1683,7 @@@
1682
1683
assert resp.get("success")
1683
1684
coins = resp["nft_list"]
1684
1685
assert len(coins) == 1
1686
++ assert coins[0].owner_did is not None
1685
1687
assert coins[0].owner_did.hex() == hex_did_id
1686
1688
nft_coin_id = coins[0].nft_coin_id
1687
1689
resp = await api_0.nft_get_info(dict(coin_id=nft_coin_id.hex(), latest=True))
@@@ -1707,15 -1707,15 +1709,12 @@@
1707
1709
assert coins[0] == resp["nft_info"]
1708
1710

1709
1711

1710
-- @pytest.mark.parametrize(
1711
-- "trusted",
1712
-- [True, False],
1713
-- )
1712
++ @pytest.mark.parametrize("trusted", [True, False])
1714
1713
@pytest.mark.anyio
1715
-- async def test_set_nft_status(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
1714
++ async def test_set_nft_status(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
1716
1715
num_blocks = 5
1717
1716
full_nodes, wallets, _ = two_wallet_nodes
1718
-- full_node_api: FullNodeSimulator = full_nodes[0]
1717
++ full_node_api = full_nodes[0]
1719
1718
full_node_server = full_node_api.server
1720
1719
wallet_node_0, server_0 = wallets[0]
1721
1720
wallet_node_1, server_1 = wallets[1]
@@@ -1776,7 -1776,7 +1775,7 @@@
1776
1775
)
1777
1776
assert coins_response["nft_list"], isinstance(coins_response, dict)
1778
1777
assert coins_response.get("success")
1779
-- coins = coins_response["nft_list"]
1778
++ coins: List[NFTInfo] = coins_response["nft_list"]
1780
1779
assert len(coins) == 1
1781
1780
assert coins[0].owner_did is None
1782
1781
assert not coins[0].pending_transaction
@@@ -1793,15 -1793,15 +1792,12 @@@
1793
1792
assert coins[0].pending_transaction
1794
1793

1795
1794

1796
-- @pytest.mark.parametrize(
1797
-- "trusted",
1798
-- [True, False],
1799
-- )
1795
++ @pytest.mark.parametrize("trusted", [True, False])
1800
1796
@pytest.mark.anyio
1801
-- async def test_nft_sign_message(self_hostname: str, two_wallet_nodes: Any, trusted: Any) -> None:
1797
++ async def test_nft_sign_message(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
1802
1798
num_blocks = 5
1803
1799
full_nodes, wallets, _ = two_wallet_nodes
1804
-- full_node_api: FullNodeSimulator = full_nodes[0]
1800
++ full_node_api = full_nodes[0]
1805
1801
full_node_server = full_node_api.server
1806
1802
wallet_node_0, server_0 = wallets[0]
1807
1803
wallet_node_1, server_1 = wallets[1]
@@@ -1862,7 -1862,7 +1858,7 @@@
1862
1858
)
1863
1859
assert coins_response["nft_list"], isinstance(coins_response, dict)
1864
1860
assert coins_response.get("success")
1865
-- coins = coins_response["nft_list"]
1861
++ coins: List[NFTInfo] = coins_response["nft_list"]
1866
1862
assert len(coins) == 1
1867
1863
assert coins[0].owner_did is None
1868
1864
assert not coins[0].pending_transaction
@@@ -1871,7 -1871,7 +1867,7 @@@
1871
1867
response = await api_0.sign_message_by_id(
1872
1868
{"id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value), "message": message}
1873
1869
)
1874
-- puzzle: Program = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, message))
1870
++ puzzle = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, message))
1875
1871
assert AugSchemeMPL.verify(
1876
1872
G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
1877
1873
puzzle.get_tree_hash(),
tests/wallet/rpc/test_wallet_rpc.py CHANGED
@@@ -536,20 -536,20 +536,36 @@@ async def test_create_signed_transactio
536
536
await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
537
537
await time_out_assert(20, get_confirmed_balance, generated_funds - amount_total, wallet_1_rpc, wallet_id)
538
538

539
-- # Validate the memos
539
++ # Assert every coin comes from the same parent
540
++ additions: List[Coin] = spend_bundle.additions()
541
++ assert len({c.parent_coin_info for c in additions}) == 2 if is_cat else 1
542
++
543
++ # Assert you can get the spend for each addition
544
++ for addition in additions:
545
++ cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
546
++ assert cr is not None
547
++ spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
548
++ addition.parent_coin_info, cr.confirmed_block_index
549
++ )
550
++ assert spend is not None
551
++
552
++ # Assert the memos are all correct
553
++ addition_dict: Dict[bytes32, Coin] = {addition.name(): addition for addition in additions}
554
++ memo_dictionary: Dict[bytes32, List[bytes]] = compute_memos(spend_bundle)
540
555
for output in outputs:
541
-- if "memos" in outputs:
556
++ if "memos" in output:
542
557
found: bool = False
543
-- for addition in spend_bundle.additions():
544
-- if addition.amount == output["amount"] and addition.puzzle_hash.hex() == output["puzzle_hash"]:
545
-- cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
546
-- assert cr is not None
547
-- spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
548
-- addition.parent_coin_info, cr.confirmed_block_index
549
-- )
550
-- assert spend is not None
551
-- sb: SpendBundle = SpendBundle([spend], G2Element())
552
-- assert compute_memos(sb) == {addition.name(): [memo.encode() for memo in output["memos"]]}
558
++ for addition_id, addition in addition_dict.items():
559
++ if (
560
++ is_cat
561
++ and addition.amount == output["amount"]
562
++ and memo_dictionary[addition_id][0] == output["puzzle_hash"]
563
++ and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output["memos"]]
564
++ ) or (
565
++ addition.amount == output["amount"]
566
++ and addition.puzzle_hash == output["puzzle_hash"]
567
++ and memo_dictionary[addition_id] == [memo.encode() for memo in output["memos"]]
568
++ ):
553
569
found = True
554
570
assert found
555
571

@@@ -913,13 -913,13 +929,12 @@@ async def test_get_transaction_count(wa
913
929
assert len(all_transactions) > 0
914
930
transaction_count = await client.get_transaction_count(1)
915
931
assert transaction_count == len(all_transactions)
916
-- assert await client.get_transaction_count(1, confirmed=False) == 0
917
-- assert (
918
-- await client.get_transaction_count(
919
-- 1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND])
920
-- )
921
-- == 0
932
++ transaction_count = await client.get_transaction_count(1, confirmed=False)
933
++ assert transaction_count == 0
934
++ transaction_count = await client.get_transaction_count(
935
++ 1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND])
922
936
)
937
++ assert transaction_count == 0
923
938

924
939

925
940
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
@@@ -1825,12 -1825,12 +1840,12 @@@ async def test_select_coins_rpc(wallet_
1825
1840
excluded_coin_ids=[c.name() for c in excluded_amt_coins]
1826
1841
),
1827
1842
)
1828
-- assert excluded_amt_coins not in all_coins
1843
++ assert set(excluded_amt_coins).intersection({rec.coin for rec in all_coins}) == set()
1829
1844
all_coins, _, _ = await client_2.get_spendable_coins(
1830
1845
wallet_id=1,
1831
1846
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
1832
1847
)
1833
-- assert excluded_amt_coins not in all_coins
1848
++ assert len([rec for rec in all_coins if rec.coin.amount == 1000]) == 0
1834
1849
all_coins_2, _, _ = await client_2.get_spendable_coins(
1835
1850
wallet_id=1,
1836
1851
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(max_coin_amount=uint64(999)),
tests/wallet/simple_sync/test_simple_sync_protocol.py CHANGED
@@@ -17,6 -17,6 +17,7 @@@ from chia.server.outbound_message impor
17
17
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
18
18
from chia.simulator.wallet_tools import WalletTool
19
19
from chia.types.blockchain_format.coin import Coin
20
++ from chia.types.blockchain_format.sized_bytes import bytes32
20
21
from chia.types.coin_record import CoinRecord
21
22
from chia.types.condition_opcodes import ConditionOpcode
22
23
from chia.types.condition_with_args import ConditionWithArgs
@@@ -30,16 -30,16 +31,10 @@@ from tests.connection_utils import add_
30
31
from tests.util.setup_nodes import OldSimulatorsAndWallets
31
32
from tests.util.time_out_assert import time_out_assert
32
33

33
--
34
-- def wallet_height_at_least(wallet_node, h):
35
-- height = wallet_node.wallet_state_manager.blockchain._peak_height
36
-- if height == h:
37
-- return True
38
-- return False
39
--
40
--
41
34
log = getLogger(__name__)
42
35

36
++ zero_ph = bytes32(32 * b"\0")
37
++
43
38

44
39
async def get_all_messages_in_queue(queue):
45
40
all_messages = []
@@@ -50,483 -50,483 +45,605 @@@
50
45
return all_messages
51
46

52
47

53
-- class TestSimpleSyncProtocol:
54
-- @pytest.mark.anyio
55
-- async def test_subscribe_for_ph(self, simulator_and_wallet, self_hostname):
56
-- num_blocks = 4
57
-- full_nodes, wallets, _ = simulator_and_wallet
58
-- full_node_api = full_nodes[0]
59
-- wallet_node, server_2 = wallets[0]
60
-- fn_server = full_node_api.full_node.server
61
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
62
--
63
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
64
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
65
--
66
-- zero_ph = 32 * b"\0"
67
-- junk_ph = 32 * b"\a"
68
-- fake_wallet_peer = fn_server.all_connections[peer_id]
69
-- msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
70
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
71
--
72
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
73
-- data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
74
-- assert data_response.coin_states == []
75
--
76
-- # Farm few more with reward
77
-- for i in range(0, num_blocks):
78
-- if i == num_blocks - 1:
79
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
80
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
81
-- else:
82
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
83
--
84
-- msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
85
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
86
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
87
-- data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
88
-- # we have already subscribed to this puzzle hash, it will be ignored
89
-- # we still receive the updates (see below)
90
-- assert data_response.coin_states == []
91
--
92
-- # Farm more rewards to check the incoming queue for the updates
93
-- for i in range(0, num_blocks):
94
-- if i == num_blocks - 1:
95
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
96
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
97
-- else:
98
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
99
--
100
-- all_messages = await get_all_messages_in_queue(incoming_queue)
101
--
102
-- zero_coin = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {zero_ph})
103
-- all_zero_coin = set(zero_coin)
104
-- notified_zero_coins = set()
105
--
106
-- for message in all_messages:
107
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
108
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
109
-- for coin_state in data_response.items:
110
-- notified_zero_coins.add(coin_state)
111
-- assert len(data_response.items) == 2 # 2 per height farmer / pool reward
112
--
113
-- assert all_zero_coin == notified_zero_coins
114
--
115
-- # Test subscribing to more coins
116
-- one_ph = 32 * b"\1"
117
-- msg = wallet_protocol.RegisterForPhUpdates([one_ph], 0)
118
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
119
-- peak = full_node_api.full_node.blockchain.get_peak()
120
--
121
-- for i in range(0, num_blocks):
122
-- if i == num_blocks - 1:
123
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
124
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
125
-- else:
126
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
127
--
128
-- for i in range(0, num_blocks):
129
-- if i == num_blocks - 1:
130
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
131
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
132
-- else:
133
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
134
--
135
-- zero_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(
136
-- True, {zero_ph}, peak.height + 1
137
-- )
138
-- one_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {one_ph})
48
++ @pytest.mark.anyio
49
++ async def test_subscribe_for_ph(simulator_and_wallet, self_hostname):
50
++ num_blocks = 4
51
++ full_nodes, wallets, _ = simulator_and_wallet
52
++ full_node_api = full_nodes[0]
53
++ wallet_node, server_2 = wallets[0]
54
++ fn_server = full_node_api.full_node.server
55
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
139
56

140
-- all_coins = set(zero_coins)
141
-- all_coins.update(one_coins)
57
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
58
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
142
59

143
-- all_messages = await get_all_messages_in_queue(incoming_queue)
60
++ junk_ph = 32 * b"\a"
61
++ fake_wallet_peer = fn_server.all_connections[peer_id]
62
++ msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
63
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
144
64

145
-- notified_all_coins = set()
146
--
147
-- for message in all_messages:
148
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
149
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
150
-- for coin_state in data_response.items:
151
-- notified_all_coins.add(coin_state)
152
-- assert len(data_response.items) == 2 # 2 per height farmer / pool reward
65
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
66
++ data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
67
++ assert data_response.coin_states == []
153
68

154
-- assert all_coins == notified_all_coins
155
--
156
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
157
-- wallet: Wallet = wsm.wallets[1]
158
-- puzzle_hash = await wallet.get_new_puzzlehash()
159
--
160
-- for i in range(0, num_blocks):
161
-- if i == num_blocks - 1:
162
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
163
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
164
-- else:
165
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
166
--
167
-- funds = sum(
168
-- [
169
-- calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
170
-- for i in range(1, num_blocks + 1)
171
-- ]
172
-- )
173
-- fn_amount = sum(
174
-- cr.coin.amount
175
-- for cr in await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, puzzle_hash)
176
-- )
177
--
178
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
179
-- assert funds == fn_amount
69
++ # Farm few more with reward
70
++ for i in range(0, num_blocks):
71
++ if i == num_blocks - 1:
72
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
73
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
74
++ else:
75
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
180
76

181
-- msg_1 = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
182
-- msg_response_1 = await full_node_api.register_interest_in_puzzle_hash(msg_1, fake_wallet_peer)
183
-- assert msg_response_1.type == ProtocolMessageTypes.respond_to_ph_update.value
184
-- data_response_1: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response_1.data)
185
-- assert len(data_response_1.coin_states) == 2 * num_blocks # 2 per height farmer / pool reward
77
++ msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
78
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
79
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
80
++ data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
81
++ # we have already subscribed to this puzzle hash, it will be ignored
82
++ # we still receive the updates (see below)
83
++ assert data_response.coin_states == []
84
++
85
++ # Farm more rewards to check the incoming queue for the updates
86
++ for i in range(0, num_blocks):
87
++ if i == num_blocks - 1:
88
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
89
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
90
++ else:
91
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
186
92

187
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
93
++ all_messages = await get_all_messages_in_queue(incoming_queue)
188
94

189
-- [tx_record] = await wallet.generate_signed_transaction(uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0))
190
-- assert len(tx_record.spend_bundle.removals()) == 1
191
-- spent_coin = tx_record.spend_bundle.removals()[0]
192
-- assert spent_coin.puzzle_hash == puzzle_hash
95
++ zero_coin = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {zero_ph})
96
++ all_zero_coin = set(zero_coin)
97
++ notified_zero_coins = set()
193
98

194
-- await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
99
++ for message in all_messages:
100
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
101
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
102
++ for coin_state in data_response.items:
103
++ notified_zero_coins.add(coin_state)
104
++ assert len(data_response.items) == 2 # 2 per height farmer / pool reward
195
105

196
-- await full_node_api.process_transaction_records(records=[tx_record])
106
++ assert all_zero_coin == notified_zero_coins
197
107

198
-- # Let's make sure the wallet can handle a non ephemeral launcher
199
-- from chia.wallet.puzzles.singleton_top_layer import SINGLETON_LAUNCHER_HASH
108
++ # Test subscribing to more coins
109
++ one_ph = 32 * b"\1"
110
++ msg = wallet_protocol.RegisterForPhUpdates([one_ph], 0)
111
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
112
++ peak = full_node_api.full_node.blockchain.get_peak()
200
113

201
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
114
++ for i in range(0, num_blocks):
115
++ if i == num_blocks - 1:
116
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
117
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
118
++ else:
119
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
202
120

203
-- [tx_record] = await wallet.generate_signed_transaction(
204
-- uint64(10), SINGLETON_LAUNCHER_HASH, DEFAULT_TX_CONFIG, uint64(0)
205
-- )
206
-- await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
121
++ for i in range(0, num_blocks):
122
++ if i == num_blocks - 1:
123
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
124
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
125
++ else:
126
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
207
127

208
-- await full_node_api.process_transaction_records(records=[tx_record])
128
++ zero_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(
129
++ True, {zero_ph}, peak.height + 1
130
++ )
131
++ one_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {one_ph})
209
132

210
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
133
++ all_coins = set(zero_coins)
134
++ all_coins.update(one_coins)
211
135

212
-- # Send a transaction to make sure the wallet is still running
213
-- [tx_record] = await wallet.generate_signed_transaction(uint64(10), junk_ph, DEFAULT_TX_CONFIG, uint64(0))
214
-- await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
136
++ all_messages = await get_all_messages_in_queue(incoming_queue)
215
137

216
-- await full_node_api.process_transaction_records(records=[tx_record])
138
++ notified_all_coins = set()
217
139

218
-- all_messages = await get_all_messages_in_queue(incoming_queue)
140
++ for message in all_messages:
141
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
142
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
143
++ for coin_state in data_response.items:
144
++ notified_all_coins.add(coin_state)
145
++ assert len(data_response.items) == 2 # 2 per height farmer / pool reward
219
146

220
-- notified_state = None
147
++ assert all_coins == notified_all_coins
221
148

222
-- for message in all_messages:
223
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
224
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
225
-- for coin_state in data_response.items:
226
-- if coin_state.coin.name() == spent_coin.name():
227
-- notified_state = coin_state
149
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
150
++ wallet: Wallet = wsm.wallets[1]
151
++ puzzle_hash = await wallet.get_new_puzzlehash()
228
152

229
-- assert notified_state is not None
230
-- assert notified_state.coin == spent_coin
231
-- assert notified_state.spent_height is not None
232
--
233
-- @pytest.mark.anyio
234
-- async def test_subscribe_for_coin_id(self, simulator_and_wallet, self_hostname):
235
-- num_blocks = 4
236
-- full_nodes, wallets, _ = simulator_and_wallet
237
-- full_node_api = full_nodes[0]
238
-- wallet_node, server_2 = wallets[0]
239
-- fn_server = full_node_api.full_node.server
240
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
241
-- standard_wallet: Wallet = wsm.wallets[1]
242
-- puzzle_hash = await standard_wallet.get_new_puzzlehash()
243
--
244
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
245
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
246
--
247
-- fake_wallet_peer = fn_server.all_connections[peer_id]
248
--
249
-- # Farm to create a coin that we'll track
250
-- for i in range(0, num_blocks):
153
++ for i in range(0, num_blocks):
154
++ if i == num_blocks - 1:
155
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
156
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
157
++ else:
251
158
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
252
159

253
-- funds = sum(
254
-- [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
255
-- )
160
++ funds = sum(
161
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
162
++ )
163
++ fn_amount = sum(
164
++ cr.coin.amount
165
++ for cr in await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, puzzle_hash)
166
++ )
256
167

257
-- await time_out_assert(20, standard_wallet.get_confirmed_balance, funds)
168
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
169
++ assert funds == fn_amount
258
170

259
-- my_coins: List[CoinRecord] = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
260
-- True, puzzle_hash
261
-- )
262
-- coin_to_spend = my_coins[0].coin
171
++ msg_1 = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
172
++ msg_response_1 = await full_node_api.register_interest_in_puzzle_hash(msg_1, fake_wallet_peer)
173
++ assert msg_response_1.type == ProtocolMessageTypes.respond_to_ph_update.value
174
++ data_response_1: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response_1.data)
175
++ assert len(data_response_1.coin_states) == 2 * num_blocks # 2 per height farmer / pool reward
263
176

264
-- msg = wallet_protocol.RegisterForCoinUpdates([coin_to_spend.name()], 0)
265
-- msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
266
-- assert msg_response is not None
267
-- assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
268
-- data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
269
-- assert data_response.coin_states[0].coin == coin_to_spend
177
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
270
178

271
-- coins = set()
272
-- coins.add(coin_to_spend)
273
-- [tx_record] = await standard_wallet.generate_signed_transaction(
274
-- uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0), coins=coins
275
-- )
276
-- await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
179
++ [tx_record] = await wallet.generate_signed_transaction(uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0))
180
++ assert len(tx_record.spend_bundle.removals()) == 1
181
++ spent_coin = tx_record.spend_bundle.removals()[0]
182
++ assert spent_coin.puzzle_hash == puzzle_hash
277
183

278
-- await full_node_api.process_transaction_records(records=[tx_record])
184
++ await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
279
185

280
-- all_messages = await get_all_messages_in_queue(incoming_queue)
186
++ await full_node_api.process_transaction_records(records=[tx_record])
281
187

282
-- notified_coins = set()
283
-- for message in all_messages:
284
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
285
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
286
-- for coin_state in data_response.items:
287
-- notified_coins.add(coin_state.coin)
288
-- assert coin_state.spent_height is not None
188
++ # Let's make sure the wallet can handle a non ephemeral launcher
189
++ from chia.wallet.puzzles.singleton_top_layer import SINGLETON_LAUNCHER_HASH
289
190

290
-- assert notified_coins == coins
191
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
291
192

292
-- # Test getting notification for coin that is about to be created
293
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
193
++ [tx_record] = await wallet.generate_signed_transaction(
194
++ uint64(10), SINGLETON_LAUNCHER_HASH, DEFAULT_TX_CONFIG, uint64(0)
195
++ )
196
++ await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
294
197

295
-- [tx_record] = await standard_wallet.generate_signed_transaction(
296
-- uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0)
297
-- )
198
++ await full_node_api.process_transaction_records(records=[tx_record])
298
199

299
-- tx_record.spend_bundle.additions()
200
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
300
201

301
-- added_target: Optional[Coin] = None
302
-- for coin in tx_record.spend_bundle.additions():
303
-- if coin.puzzle_hash == puzzle_hash:
304
-- added_target = coin
202
++ # Send a transaction to make sure the wallet is still running
203
++ [tx_record] = await wallet.generate_signed_transaction(uint64(10), junk_ph, DEFAULT_TX_CONFIG, uint64(0))
204
++ await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
305
205

306
-- assert added_target is not None
206
++ await full_node_api.process_transaction_records(records=[tx_record])
307
207

308
-- msg = wallet_protocol.RegisterForCoinUpdates([added_target.name()], 0)
309
-- msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
310
-- assert msg_response is not None
311
-- assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
312
-- data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
313
-- assert len(data_response.coin_states) == 0
208
++ all_messages = await get_all_messages_in_queue(incoming_queue)
314
209

315
-- await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
210
++ notified_state = None
316
211

317
-- await full_node_api.process_transaction_records(records=[tx_record])
212
++ for message in all_messages:
213
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
214
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
215
++ for coin_state in data_response.items:
216
++ if coin_state.coin.name() == spent_coin.name():
217
++ notified_state = coin_state
318
218

319
-- all_messages = await get_all_messages_in_queue(incoming_queue)
219
++ assert notified_state is not None
220
++ assert notified_state.coin == spent_coin
221
++ assert notified_state.spent_height is not None
320
222

321
-- notified_state = None
322
223

323
-- for message in all_messages:
324
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
325
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
326
-- for coin_state in data_response.items:
327
-- if coin_state.coin.name() == added_target.name():
328
-- notified_state = coin_state
224
++ @pytest.mark.anyio
225
++ async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname):
226
++ num_blocks = 4
227
++ full_nodes, wallets, _ = simulator_and_wallet
228
++ full_node_api = full_nodes[0]
229
++ wallet_node, server_2 = wallets[0]
230
++ fn_server = full_node_api.full_node.server
231
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
232
++ standard_wallet: Wallet = wsm.wallets[1]
233
++ puzzle_hash = await standard_wallet.get_new_puzzlehash()
329
234

330
-- assert notified_state is not None
331
-- assert notified_state.coin == added_target
332
-- assert notified_state.spent_height is None
333
--
334
-- @pytest.mark.anyio
335
-- async def test_subscribe_for_ph_reorg(self, simulator_and_wallet, self_hostname):
336
-- num_blocks = 4
337
-- long_blocks = 20
338
-- full_nodes, wallets, _ = simulator_and_wallet
339
-- full_node_api = full_nodes[0]
340
-- wallet_node, server_2 = wallets[0]
341
-- fn_server = full_node_api.full_node.server
342
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
343
-- standard_wallet: Wallet = wsm.wallets[1]
344
-- puzzle_hash = await standard_wallet.get_new_puzzlehash()
345
--
346
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
347
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
348
--
349
-- fake_wallet_peer = fn_server.all_connections[peer_id]
350
-- zero_ph = 32 * b"\0"
351
--
352
-- # Farm to create a coin that we'll track
353
-- for i in range(0, num_blocks):
354
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
235
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
236
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
355
237

356
-- for i in range(0, long_blocks):
357
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
238
++ fake_wallet_peer = fn_server.all_connections[peer_id]
358
239

359
-- msg = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
360
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
361
-- assert msg_response is not None
240
++ # Farm to create a coin that we'll track
241
++ for i in range(0, num_blocks):
362
242
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
363
243

364
-- for i in range(0, num_blocks):
365
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
244
++ funds = sum(
245
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
246
++ )
366
247

367
-- expected_height = uint32(long_blocks + 2 * num_blocks + 1)
368
-- await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
248
++ await time_out_assert(20, standard_wallet.get_confirmed_balance, funds)
369
249

370
-- coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
371
-- assert len(coin_records) > 0
372
-- fork_height = expected_height - num_blocks - 5
373
-- req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
374
-- await full_node_api.reorg_from_index_to_new_index(req)
250
++ my_coins: List[CoinRecord] = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
251
++ True, puzzle_hash
252
++ )
253
++ coin_to_spend = my_coins[0].coin
375
254

376
-- coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
377
-- assert coin_records == []
255
++ msg = wallet_protocol.RegisterForCoinUpdates([coin_to_spend.name()], 0)
256
++ msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
257
++ assert msg_response is not None
258
++ assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
259
++ data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
260
++ assert data_response.coin_states[0].coin == coin_to_spend
378
261

379
-- all_messages = await get_all_messages_in_queue(incoming_queue)
262
++ coins = set()
263
++ coins.add(coin_to_spend)
264
++ [tx_record] = await standard_wallet.generate_signed_transaction(
265
++ uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0), coins=coins
266
++ )
267
++ await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
380
268

381
-- coin_update_messages = []
382
-- for message in all_messages:
383
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
384
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
385
-- coin_update_messages.append(data_response)
386
--
387
-- # First state is creation, second one is a reorg
388
-- assert len(coin_update_messages) == 2
389
-- first = coin_update_messages[0]
390
--
391
-- assert len(first.items) == 2
392
-- first_state_coin_1 = first.items[0]
393
-- assert first_state_coin_1.spent_height is None
394
-- assert first_state_coin_1.created_height is not None
395
-- first_state_coin_2 = first.items[1]
396
-- assert first_state_coin_2.spent_height is None
397
-- assert first_state_coin_2.created_height is not None
398
--
399
-- second = coin_update_messages[1]
400
-- assert second.fork_height == fork_height
401
-- assert len(second.items) == 2
402
-- second_state_coin_1 = second.items[0]
403
-- assert second_state_coin_1.spent_height is None
404
-- assert second_state_coin_1.created_height is None
405
-- second_state_coin_2 = second.items[1]
406
-- assert second_state_coin_2.spent_height is None
407
-- assert second_state_coin_2.created_height is None
408
--
409
-- @pytest.mark.anyio
410
-- async def test_subscribe_for_coin_id_reorg(self, simulator_and_wallet, self_hostname):
411
-- num_blocks = 4
412
-- long_blocks = 20
413
-- full_nodes, wallets, _ = simulator_and_wallet
414
-- full_node_api = full_nodes[0]
415
-- wallet_node, server_2 = wallets[0]
416
-- fn_server = full_node_api.full_node.server
417
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
418
-- standard_wallet: Wallet = wsm.wallets[1]
419
-- puzzle_hash = await standard_wallet.get_new_puzzlehash()
420
--
421
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
422
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
423
--
424
-- fake_wallet_peer = fn_server.all_connections[peer_id]
425
-- zero_ph = 32 * b"\0"
426
--
427
-- # Farm to create a coin that we'll track
428
-- for i in range(0, num_blocks):
429
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
269
++ await full_node_api.process_transaction_records(records=[tx_record])
430
270

431
-- for i in range(0, long_blocks):
432
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
271
++ all_messages = await get_all_messages_in_queue(incoming_queue)
433
272

434
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
273
++ notified_coins = set()
274
++ for message in all_messages:
275
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
276
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
277
++ for coin_state in data_response.items:
278
++ notified_coins.add(coin_state.coin)
279
++ assert coin_state.spent_height is not None
435
280

436
-- for i in range(0, num_blocks):
437
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
281
++ assert notified_coins == coins
438
282

439
-- expected_height = uint32(long_blocks + 2 * num_blocks + 1)
440
-- await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
283
++ # Test getting notification for coin that is about to be created
284
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
441
285

442
-- coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
443
-- assert len(coin_records) > 0
286
++ [tx_record] = await standard_wallet.generate_signed_transaction(
287
++ uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0)
288
++ )
444
289

445
-- for coin_rec in coin_records:
446
-- msg = wallet_protocol.RegisterForCoinUpdates([coin_rec.name], 0)
447
-- msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
448
-- assert msg_response is not None
290
++ added_target: Optional[Coin] = None
291
++ for coin in tx_record.spend_bundle.additions():
292
++ if coin.puzzle_hash == puzzle_hash:
293
++ added_target = coin
294
++
295
++ assert added_target is not None
296
++
297
++ msg = wallet_protocol.RegisterForCoinUpdates([added_target.name()], 0)
298
++ msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
299
++ assert msg_response is not None
300
++ assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
301
++ data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
302
++ assert len(data_response.coin_states) == 0
303
++
304
++ await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
305
++
306
++ await full_node_api.process_transaction_records(records=[tx_record])
307
++
308
++ all_messages = await get_all_messages_in_queue(incoming_queue)
309
++
310
++ notified_state = None
311
++
312
++ for message in all_messages:
313
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
314
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
315
++ for coin_state in data_response.items:
316
++ if coin_state.coin.name() == added_target.name():
317
++ notified_state = coin_state
318
++
319
++ assert notified_state is not None
320
++ assert notified_state.coin == added_target
321
++ assert notified_state.spent_height is None
322
++
323
++
324
++ @pytest.mark.anyio
325
++ async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname):
326
++ num_blocks = 4
327
++ long_blocks = 20
328
++ full_nodes, wallets, _ = simulator_and_wallet
329
++ full_node_api = full_nodes[0]
330
++ wallet_node, server_2 = wallets[0]
331
++ fn_server = full_node_api.full_node.server
332
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
333
++ standard_wallet: Wallet = wsm.wallets[1]
334
++ puzzle_hash = await standard_wallet.get_new_puzzlehash()
335
++
336
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
337
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
338
++
339
++ fake_wallet_peer = fn_server.all_connections[peer_id]
340
++
341
++ # Farm to create a coin that we'll track
342
++ for i in range(0, num_blocks):
343
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
344
++
345
++ for i in range(0, long_blocks):
346
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
347
++
348
++ msg = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
349
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
350
++ assert msg_response is not None
351
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
352
++
353
++ for i in range(0, num_blocks):
354
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
355
++
356
++ expected_height = uint32(long_blocks + 2 * num_blocks + 1)
357
++ await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
358
++
359
++ coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
360
++ assert len(coin_records) > 0
361
++ fork_height = expected_height - num_blocks - 5
362
++ req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
363
++ await full_node_api.reorg_from_index_to_new_index(req)
364
++
365
++ coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
366
++ assert coin_records == []
449
367

450
-- fork_height = expected_height - num_blocks - 5
451
-- req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
452
-- await full_node_api.reorg_from_index_to_new_index(req)
368
++ all_messages = await get_all_messages_in_queue(incoming_queue)
453
369

454
-- coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
455
-- assert coin_records == []
370
++ coin_update_messages = []
371
++ for message in all_messages:
372
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
373
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
374
++ coin_update_messages.append(data_response)
375
++
376
++ # First state is creation, second one is a reorg
377
++ assert len(coin_update_messages) == 2
378
++ first = coin_update_messages[0]
456
379

457
-- all_messages = await get_all_messages_in_queue(incoming_queue)
380
++ assert len(first.items) == 2
381
++ first_state_coin_1 = first.items[0]
382
++ assert first_state_coin_1.spent_height is None
383
++ assert first_state_coin_1.created_height is not None
384
++ first_state_coin_2 = first.items[1]
385
++ assert first_state_coin_2.spent_height is None
386
++ assert first_state_coin_2.created_height is not None
387
++
388
++ second = coin_update_messages[1]
389
++ assert second.fork_height == fork_height
390
++ assert len(second.items) == 2
391
++ second_state_coin_1 = second.items[0]
392
++ assert second_state_coin_1.spent_height is None
393
++ assert second_state_coin_1.created_height is None
394
++ second_state_coin_2 = second.items[1]
395
++ assert second_state_coin_2.spent_height is None
396
++ assert second_state_coin_2.created_height is None
397
++
398
++
399
++ @pytest.mark.anyio
400
++ async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname):
401
++ num_blocks = 4
402
++ long_blocks = 20
403
++ full_nodes, wallets, _ = simulator_and_wallet
404
++ full_node_api = full_nodes[0]
405
++ wallet_node, server_2 = wallets[0]
406
++ fn_server = full_node_api.full_node.server
407
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
408
++ standard_wallet: Wallet = wsm.wallets[1]
409
++ puzzle_hash = await standard_wallet.get_new_puzzlehash()
410
++
411
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
412
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
413
++
414
++ fake_wallet_peer = fn_server.all_connections[peer_id]
415
++
416
++ # Farm to create a coin that we'll track
417
++ for i in range(0, num_blocks):
418
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
419
++
420
++ for i in range(0, long_blocks):
421
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
422
++
423
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
424
++
425
++ for i in range(0, num_blocks):
426
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
427
++
428
++ expected_height = uint32(long_blocks + 2 * num_blocks + 1)
429
++ await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
430
++
431
++ coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
432
++ assert len(coin_records) > 0
458
433

459
-- coin_update_messages = []
460
-- for message in all_messages:
461
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
462
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
463
-- coin_update_messages.append(data_response)
464
--
465
-- assert len(coin_update_messages) == 1
466
-- update = coin_update_messages[0]
467
-- coin_states = update.items
468
-- assert len(coin_states) == 2
469
-- first_coin = coin_states[0]
470
-- assert first_coin.spent_height is None
471
-- assert first_coin.created_height is None
472
-- second_coin = coin_states[1]
473
-- assert second_coin.spent_height is None
474
-- assert second_coin.created_height is None
475
--
476
-- @pytest.mark.anyio
477
-- async def test_subscribe_for_hint(self, simulator_and_wallet, self_hostname):
478
-- num_blocks = 4
479
-- full_nodes, wallets, bt = simulator_and_wallet
480
-- full_node_api = full_nodes[0]
481
-- wallet_node, server_2 = wallets[0]
482
-- fn_server = full_node_api.full_node.server
483
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
484
--
485
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
486
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
487
--
488
-- wt: WalletTool = bt.get_pool_wallet_tool()
489
-- ph = wt.get_new_puzzlehash()
490
-- for i in range(0, num_blocks):
491
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
492
--
493
-- await asyncio.sleep(6)
494
-- coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
495
-- coin_spent = coins[0].coin
496
-- hint_puzzle_hash = 32 * b"\2"
497
-- amount = 1
498
-- amount_bin = int_to_bytes(1)
499
-- hint = 32 * b"\5"
500
--
501
-- fake_wallet_peer = fn_server.all_connections[peer_id]
502
-- msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
503
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
504
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
505
-- data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
506
-- assert len(data_response.coin_states) == 0
507
--
508
-- condition_dict = {
509
-- ConditionOpcode.CREATE_COIN: [
510
-- ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
511
-- ]
512
-- }
513
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
514
--
515
-- tx: SpendBundle = wt.generate_signed_transaction(
516
-- 10,
517
-- wt.get_new_puzzlehash(),
518
-- coin_spent,
519
-- condition_dic=condition_dict,
520
-- )
521
-- await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
522
--
523
-- await full_node_api.process_spend_bundles(bundles=[tx])
524
--
525
-- all_messages = await get_all_messages_in_queue(incoming_queue)
434
++ for coin_rec in coin_records:
435
++ msg = wallet_protocol.RegisterForCoinUpdates([coin_rec.name], 0)
436
++ msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
437
++ assert msg_response is not None
526
438

439
++ fork_height = expected_height - num_blocks - 5
440
++ req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
441
++ await full_node_api.reorg_from_index_to_new_index(req)
442
++
443
++ coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
444
++ assert coin_records == []
445
++
446
++ all_messages = await get_all_messages_in_queue(incoming_queue)
447
++
448
++ coin_update_messages = []
449
++ for message in all_messages:
450
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
451
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
452
++ coin_update_messages.append(data_response)
453
++
454
++ assert len(coin_update_messages) == 1
455
++ update = coin_update_messages[0]
456
++ coin_states = update.items
457
++ assert len(coin_states) == 2
458
++ first_coin = coin_states[0]
459
++ assert first_coin.spent_height is None
460
++ assert first_coin.created_height is None
461
++ second_coin = coin_states[1]
462
++ assert second_coin.spent_height is None
463
++ assert second_coin.created_height is None
464
++
465
++
466
++ @pytest.mark.anyio
467
++ async def test_subscribe_for_hint(simulator_and_wallet, self_hostname):
468
++ num_blocks = 4
469
++ full_nodes, wallets, bt = simulator_and_wallet
470
++ full_node_api = full_nodes[0]
471
++ wallet_node, server_2 = wallets[0]
472
++ fn_server = full_node_api.full_node.server
473
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
474
++
475
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
476
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
477
++
478
++ wt: WalletTool = bt.get_pool_wallet_tool()
479
++ ph = wt.get_new_puzzlehash()
480
++ for i in range(0, num_blocks):
481
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
482
++
483
++ await asyncio.sleep(6)
484
++ coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
485
++ coin_spent = coins[0].coin
486
++ hint_puzzle_hash = 32 * b"\2"
487
++ amount = 1
488
++ amount_bin = int_to_bytes(1)
489
++ hint = 32 * b"\5"
490
++
491
++ fake_wallet_peer = fn_server.all_connections[peer_id]
492
++ msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
493
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
494
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
495
++ data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
496
++ assert len(data_response.coin_states) == 0
497
++
498
++ condition_dict = {
499
++ ConditionOpcode.CREATE_COIN: [
500
++ ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
501
++ ]
502
++ }
503
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
504
++
505
++ tx: SpendBundle = wt.generate_signed_transaction(
506
++ 10,
507
++ wt.get_new_puzzlehash(),
508
++ coin_spent,
509
++ condition_dic=condition_dict,
510
++ )
511
++ await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
512
++
513
++ await full_node_api.process_spend_bundles(bundles=[tx])
514
++
515
++ all_messages = await get_all_messages_in_queue(incoming_queue)
516
++
517
++ notified_state = None
518
++
519
++ for message in all_messages:
520
++ if message.type == ProtocolMessageTypes.coin_state_update.value:
521
++ data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
522
++ notified_state = data_response
523
++ break
524
++
525
++ assert notified_state is not None
526
++ assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
527
++
528
++ msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
529
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
530
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
531
++ data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
532
++ # we have already subscribed to this puzzle hash. The full node will
533
++ # ignore the duplicate
534
++ assert data_response.coin_states == []
535
++
536
++
537
++ @pytest.mark.anyio
538
++ async def test_subscribe_for_puzzle_hash_coin_hint_duplicates(
539
++ simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str
540
++ ) -> None:
541
++ [full_node_api], [[_, wallet_server]], bt = simulator_and_wallet
542
++ full_node_server = full_node_api.full_node.server
543
++
544
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
545
++
546
++ wt: WalletTool = bt.get_pool_wallet_tool()
547
++ ph = wt.get_new_puzzlehash()
548
++ await full_node_api.farm_blocks_to_puzzlehash(4, ph)
549
++ coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
550
++ wallet_connection = full_node_server.all_connections[wallet_server.node_id]
551
++
552
++ # Create a coin which is hinted with its own destination puzzle hash
553
++ tx: SpendBundle = wt.generate_signed_transaction(
554
++ uint64(10),
555
++ wt.get_new_puzzlehash(),
556
++ coins[0].coin,
557
++ condition_dic={
558
++ ConditionOpcode.CREATE_COIN: [ConditionWithArgs(ConditionOpcode.CREATE_COIN, [ph, int_to_bytes(1), ph])]
559
++ },
560
++ )
561
++ await full_node_api.respond_transaction(RespondTransaction(tx), wallet_connection)
562
++ await full_node_api.process_spend_bundles(bundles=[tx])
563
++ # Query the coin states and make sure it doesn't contain duplicated entries
564
++ msg = wallet_protocol.RegisterForPhUpdates([ph], uint32(0))
565
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, wallet_connection)
566
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
567
++ response = RespondToCoinUpdates.from_bytes(msg_response.data)
568
++ assert len(response.coin_states) > 0
569
++ assert len(set(response.coin_states)) == len(response.coin_states)
570
++
571
++
572
++ @pytest.mark.anyio
573
++ async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_hostname):
574
++ num_blocks = 4
575
++ full_nodes, wallets, bt = wallet_two_node_simulator
576
++ full_node_api = full_nodes[0]
577
++ full_node_api_1 = full_nodes[1]
578
++
579
++ wallet_node, server_2 = wallets[0]
580
++ fn_server = full_node_api.full_node.server
581
++ fn_server_1 = full_node_api_1.full_node.server
582
++
583
++ wsm: WalletStateManager = wallet_node.wallet_state_manager
584
++
585
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
586
++ incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
587
++ incoming_queue_1, peer_id_1 = await add_dummy_connection(fn_server_1, self_hostname, 12313, NodeType.WALLET)
588
++
589
++ wt: WalletTool = bt.get_pool_wallet_tool()
590
++ ph = wt.get_new_puzzlehash()
591
++ for i in range(0, num_blocks):
592
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
593
++
594
++ await asyncio.sleep(6)
595
++ coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
596
++ coin_spent = coins[0].coin
597
++ hint_puzzle_hash = 32 * b"\2"
598
++ amount = 1
599
++ amount_bin = int_to_bytes(1)
600
++ hint = 32 * b"\5"
601
++
602
++ fake_wallet_peer = fn_server.all_connections[peer_id]
603
++ fake_wallet_peer_1 = fn_server_1.all_connections[peer_id_1]
604
++ msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
605
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
606
++ msg_response_1 = await full_node_api_1.register_interest_in_puzzle_hash(msg, fake_wallet_peer_1)
607
++
608
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
609
++ data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
610
++ assert len(data_response.coin_states) == 0
611
++
612
++ condition_dict = {
613
++ ConditionOpcode.CREATE_COIN: [
614
++ ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
615
++ ]
616
++ }
617
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
618
++
619
++ tx: SpendBundle = wt.generate_signed_transaction(
620
++ 10,
621
++ wt.get_new_puzzlehash(),
622
++ coin_spent,
623
++ condition_dic=condition_dict,
624
++ )
625
++ await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
626
++
627
++ await full_node_api.process_spend_bundles(bundles=[tx])
628
++
629
++ # Create more blocks than recent "short_sync_blocks_behind_threshold" so that node enters batch
630
++ for i in range(0, 100):
631
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
632
++
633
++ node1_height = full_node_api_1.full_node.blockchain.get_peak_height()
634
++ assert node1_height is None
635
++
636
++ await fn_server_1.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
637
++ node0_height = full_node_api.full_node.blockchain.get_peak_height()
638
++ await time_out_assert(60, full_node_api_1.full_node.blockchain.get_peak_height, node0_height)
639
++
640
++ all_messages = await get_all_messages_in_queue(incoming_queue)
641
++ all_messages_1 = await get_all_messages_in_queue(incoming_queue_1)
642
++
643
++ def check_messages_for_hint(messages):
527
644
notified_state = None
528
645

529
-- for message in all_messages:
646
++ for message in messages:
530
647
if message.type == ProtocolMessageTypes.coin_state_update.value:
531
648
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
532
649
notified_state = data_response
@@@ -535,210 -535,210 +652,87 @@@
535
652
assert notified_state is not None
536
653
assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
537
654

538
-- msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
539
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
540
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
541
-- data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
542
-- # we have already subscribed to this puzzle hash. The full node will
543
-- # ignore the duplicate
544
-- assert data_response.coin_states == []
545
--
546
-- @pytest.mark.anyio
547
-- async def test_subscribe_for_puzzle_hash_coin_hint_duplicates(
548
-- self, simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str
549
-- ) -> None:
550
-- [full_node_api], [[_, wallet_server]], bt = simulator_and_wallet
551
-- full_node_server = full_node_api.full_node.server
552
--
553
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
554
--
555
-- wt: WalletTool = bt.get_pool_wallet_tool()
556
-- ph = wt.get_new_puzzlehash()
557
-- await full_node_api.farm_blocks_to_puzzlehash(4, ph)
558
-- coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
559
-- wallet_connection = full_node_server.all_connections[wallet_server.node_id]
560
--
561
-- # Create a coin which is hinted with its own destination puzzle hash
562
-- tx: SpendBundle = wt.generate_signed_transaction(
563
-- uint64(10),
564
-- wt.get_new_puzzlehash(),
565
-- coins[0].coin,
566
-- condition_dic={
567
-- ConditionOpcode.CREATE_COIN: [ConditionWithArgs(ConditionOpcode.CREATE_COIN, [ph, int_to_bytes(1), ph])]
568
-- },
569
-- )
570
-- await full_node_api.respond_transaction(RespondTransaction(tx), wallet_connection)
571
-- await full_node_api.process_spend_bundles(bundles=[tx])
572
-- # Query the coin states and make sure it doesn't contain duplicated entries
573
-- msg = wallet_protocol.RegisterForPhUpdates([ph], uint32(0))
574
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, wallet_connection)
575
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
576
-- response = RespondToCoinUpdates.from_bytes(msg_response.data)
577
-- assert len(response.coin_states) > 0
578
-- assert len(set(response.coin_states)) == len(response.coin_states)
579
--
580
-- @pytest.mark.anyio
581
-- async def test_subscribe_for_hint_long_sync(self, wallet_two_node_simulator, self_hostname):
582
-- num_blocks = 4
583
-- full_nodes, wallets, bt = wallet_two_node_simulator
584
-- full_node_api = full_nodes[0]
585
-- full_node_api_1 = full_nodes[1]
586
--
587
-- wallet_node, server_2 = wallets[0]
588
-- fn_server = full_node_api.full_node.server
589
-- fn_server_1 = full_node_api_1.full_node.server
590
--
591
-- wsm: WalletStateManager = wallet_node.wallet_state_manager
592
--
593
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
594
-- incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
595
-- incoming_queue_1, peer_id_1 = await add_dummy_connection(fn_server_1, self_hostname, 12313, NodeType.WALLET)
596
--
597
-- wt: WalletTool = bt.get_pool_wallet_tool()
598
-- ph = wt.get_new_puzzlehash()
599
-- for i in range(0, num_blocks):
600
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
601
--
602
-- await asyncio.sleep(6)
603
-- coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
604
-- coin_spent = coins[0].coin
605
-- hint_puzzle_hash = 32 * b"\2"
606
-- amount = 1
607
-- amount_bin = int_to_bytes(1)
608
-- hint = 32 * b"\5"
609
--
610
-- fake_wallet_peer = fn_server.all_connections[peer_id]
611
-- fake_wallet_peer_1 = fn_server_1.all_connections[peer_id_1]
612
-- msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
613
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
614
-- msg_response_1 = await full_node_api_1.register_interest_in_puzzle_hash(msg, fake_wallet_peer_1)
615
--
616
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
617
-- data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
618
-- assert len(data_response.coin_states) == 0
619
--
620
-- condition_dict = {
621
-- ConditionOpcode.CREATE_COIN: [
622
-- ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
623
-- ]
624
-- }
625
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
626
--
627
-- tx: SpendBundle = wt.generate_signed_transaction(
628
-- 10,
629
-- wt.get_new_puzzlehash(),
630
-- coin_spent,
631
-- condition_dic=condition_dict,
632
-- )
633
-- await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
634
--
635
-- await full_node_api.process_spend_bundles(bundles=[tx])
636
--
637
-- # Create more blocks than recent "short_sync_blocks_behind_threshold" so that node enters batch
638
-- for i in range(0, 100):
639
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
640
--
641
-- node1_height = full_node_api_1.full_node.blockchain.get_peak_height()
642
-- assert node1_height is None
643
--
644
-- await fn_server_1.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
645
-- node0_height = full_node_api.full_node.blockchain.get_peak_height()
646
-- await time_out_assert(60, full_node_api_1.full_node.blockchain.get_peak_height, node0_height)
647
--
648
-- all_messages = await get_all_messages_in_queue(incoming_queue)
649
-- all_messages_1 = await get_all_messages_in_queue(incoming_queue_1)
650
--
651
-- def check_messages_for_hint(messages):
652
-- notified_state = None
653
--
654
-- for message in messages:
655
-- if message.type == ProtocolMessageTypes.coin_state_update.value:
656
-- data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
657
-- notified_state = data_response
658
-- break
659
--
660
-- assert notified_state is not None
661
-- assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
662
--
663
-- check_messages_for_hint(all_messages)
664
-- check_messages_for_hint(all_messages_1)
665
--
666
-- @pytest.mark.anyio
667
-- async def test_ph_subscribe_limits(self, simulator_and_wallet, self_hostname):
668
-- full_nodes, wallets, _ = simulator_and_wallet
669
-- full_node_api = full_nodes[0]
670
-- wallet_node, server_2 = wallets[0]
671
-- fn_server = full_node_api.full_node.server
672
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
673
-- con = list(fn_server.all_connections.values())[0]
674
-- phs = []
675
-- phs.append(32 * b"\0")
676
-- phs.append(32 * b"\1")
677
-- phs.append(32 * b"\2")
678
-- phs.append(32 * b"\3")
679
-- phs.append(32 * b"\4")
680
-- phs.append(32 * b"\5")
681
-- phs.append(32 * b"\6")
682
-- full_node_api.full_node.config["max_subscribe_items"] = 2
683
-- assert full_node_api.is_trusted(con) is False
684
-- msg = wallet_protocol.RegisterForPhUpdates(phs, 0)
685
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
686
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
687
-- s = full_node_api.full_node.subscriptions
688
-- assert len(s._ph_subscriptions) == 2
689
-- assert s.has_ph_subscription(phs[0])
690
-- assert s.has_ph_subscription(phs[1])
691
-- assert not s.has_ph_subscription(phs[2])
692
-- assert not s.has_ph_subscription(phs[3])
693
-- full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
694
-- full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
695
-- assert full_node_api.is_trusted(con) is True
696
-- msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
697
-- assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
698
-- assert len(s._ph_subscriptions) == 4
699
-- assert s.has_ph_subscription(phs[0])
700
-- assert s.has_ph_subscription(phs[1])
701
-- assert s.has_ph_subscription(phs[2])
702
-- assert s.has_ph_subscription(phs[3])
703
-- assert not s.has_ph_subscription(phs[4])
704
-- assert not s.has_ph_subscription(phs[5])
705
--
706
-- @pytest.mark.anyio
707
-- async def test_coin_subscribe_limits(self, simulator_and_wallet, self_hostname):
708
-- full_nodes, wallets, _ = simulator_and_wallet
709
-- full_node_api = full_nodes[0]
710
-- wallet_node, server_2 = wallets[0]
711
-- fn_server = full_node_api.full_node.server
712
-- await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
713
-- con = list(fn_server.all_connections.values())[0]
714
-- coins = []
715
-- coins.append(32 * b"\0")
716
-- coins.append(32 * b"\1")
717
-- coins.append(32 * b"\2")
718
-- coins.append(32 * b"\3")
719
-- coins.append(32 * b"\4")
720
-- coins.append(32 * b"\5")
721
-- coins.append(32 * b"\6")
722
-- full_node_api.full_node.config["max_subscribe_items"] = 2
723
-- assert full_node_api.is_trusted(con) is False
724
-- msg = wallet_protocol.RegisterForCoinUpdates(coins, 0)
725
-- msg_response = await full_node_api.register_interest_in_coin(msg, con)
726
-- assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
727
-- s = full_node_api.full_node.subscriptions
728
-- assert len(s._coin_subscriptions) == 2
729
-- assert s.has_coin_subscription(coins[0])
730
-- assert s.has_coin_subscription(coins[1])
731
-- assert not s.has_coin_subscription(coins[2])
732
-- assert not s.has_coin_subscription(coins[3])
733
-- full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
734
-- full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
735
-- assert full_node_api.is_trusted(con) is True
736
-- msg_response = await full_node_api.register_interest_in_coin(msg, con)
737
-- assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
738
-- assert len(s._coin_subscriptions) == 4
739
-- assert s.has_coin_subscription(coins[0])
740
-- assert s.has_coin_subscription(coins[1])
741
-- assert s.has_coin_subscription(coins[2])
742
-- assert s.has_coin_subscription(coins[3])
743
-- assert not s.has_coin_subscription(coins[4])
744
-- assert not s.has_coin_subscription(coins[5])
655
++ check_messages_for_hint(all_messages)
656
++ check_messages_for_hint(all_messages_1)
657
++
658
++
659
++ @pytest.mark.anyio
660
++ async def test_ph_subscribe_limits(simulator_and_wallet, self_hostname):
661
++ full_nodes, wallets, _ = simulator_and_wallet
662
++ full_node_api = full_nodes[0]
663
++ wallet_node, server_2 = wallets[0]
664
++ fn_server = full_node_api.full_node.server
665
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
666
++ con = list(fn_server.all_connections.values())[0]
667
++ phs = []
668
++ phs.append(32 * b"\0")
669
++ phs.append(32 * b"\1")
670
++ phs.append(32 * b"\2")
671
++ phs.append(32 * b"\3")
672
++ phs.append(32 * b"\4")
673
++ phs.append(32 * b"\5")
674
++ phs.append(32 * b"\6")
675
++ full_node_api.full_node.config["max_subscribe_items"] = 2
676
++ assert full_node_api.is_trusted(con) is False
677
++ msg = wallet_protocol.RegisterForPhUpdates(phs, 0)
678
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
679
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
680
++ s = full_node_api.full_node.subscriptions
681
++ assert s.puzzle_subscription_count() == 2
682
++ assert s.has_puzzle_subscription(phs[0])
683
++ assert s.has_puzzle_subscription(phs[1])
684
++ assert not s.has_puzzle_subscription(phs[2])
685
++ assert not s.has_puzzle_subscription(phs[3])
686
++ full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
687
++ full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
688
++ assert full_node_api.is_trusted(con) is True
689
++ msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
690
++ assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
691
++ assert s.puzzle_subscription_count() == 4
692
++ assert s.has_puzzle_subscription(phs[0])
693
++ assert s.has_puzzle_subscription(phs[1])
694
++ assert s.has_puzzle_subscription(phs[2])
695
++ assert s.has_puzzle_subscription(phs[3])
696
++ assert not s.has_puzzle_subscription(phs[4])
697
++ assert not s.has_puzzle_subscription(phs[5])
698
++
699
++
700
++ @pytest.mark.anyio
701
++ async def test_coin_subscribe_limits(simulator_and_wallet, self_hostname):
702
++ full_nodes, wallets, _ = simulator_and_wallet
703
++ full_node_api = full_nodes[0]
704
++ wallet_node, server_2 = wallets[0]
705
++ fn_server = full_node_api.full_node.server
706
++ await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
707
++ con = list(fn_server.all_connections.values())[0]
708
++ coins = []
709
++ coins.append(32 * b"\0")
710
++ coins.append(32 * b"\1")
711
++ coins.append(32 * b"\2")
712
++ coins.append(32 * b"\3")
713
++ coins.append(32 * b"\4")
714
++ coins.append(32 * b"\5")
715
++ coins.append(32 * b"\6")
716
++ full_node_api.full_node.config["max_subscribe_items"] = 2
717
++ assert full_node_api.is_trusted(con) is False
718
++ msg = wallet_protocol.RegisterForCoinUpdates(coins, 0)
719
++ msg_response = await full_node_api.register_interest_in_coin(msg, con)
720
++ assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
721
++ s = full_node_api.full_node.subscriptions
722
++ assert s.coin_subscription_count() == 2
723
++ assert s.has_coin_subscription(coins[0])
724
++ assert s.has_coin_subscription(coins[1])
725
++ assert not s.has_coin_subscription(coins[2])
726
++ assert not s.has_coin_subscription(coins[3])
727
++ full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
728
++ full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
729
++ assert full_node_api.is_trusted(con) is True
730
++ msg_response = await full_node_api.register_interest_in_coin(msg, con)
731
++ assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
732
++ assert s.coin_subscription_count() == 4
733
++ assert s.has_coin_subscription(coins[0])
734
++ assert s.has_coin_subscription(coins[1])
735
++ assert s.has_coin_subscription(coins[2])
736
++ assert s.has_coin_subscription(coins[3])
737
++ assert not s.has_coin_subscription(coins[4])
738
++ assert not s.has_coin_subscription(coins[5])
tests/wallet/sync/test_wallet_sync.py CHANGED
@@@ -31,17 -31,17 +31,17 @@@ from chia.wallet.util.compute_memos imp
31
31
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
32
32
from chia.wallet.util.wallet_sync_utils import PeerRequestException
33
33
from chia.wallet.wallet_coin_record import WalletCoinRecord
34
++ from chia.wallet.wallet_state_manager import WalletStateManager
34
35
from chia.wallet.wallet_weight_proof_handler import get_wp_fork_point
35
36
from tests.connection_utils import disconnect_all, disconnect_all_and_reconnect
37
++ from tests.util.misc import wallet_height_at_least
36
38
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
37
39
from tests.weight_proof.test_weight_proof import load_blocks_dont_validate
38
40

39
41

40
-- async def wallet_height_at_least(wallet_node, h):
41
-- height = await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()
42
-- if height == h:
43
-- return True
44
-- return False
42
++ async def get_tx_count(wsm: WalletStateManager, wallet_id: int) -> int:
43
++ txs = await wsm.get_all_transactions(wallet_id)
44
++ return len(txs)
45
45

46
46

47
47
async def get_nft_count(wallet: NFTWallet) -> int:
@@@ -51,758 -51,758 +51,713 @@@
51
51
log = getLogger(__name__)
52
52

53
53

54
-- class TestWalletSync:
55
-- @pytest.mark.limit_consensus_modes(reason="save time")
56
-- @pytest.mark.anyio
57
-- async def test_request_block_headers(self, simulator_and_wallet, default_1000_blocks):
58
-- # Tests the edge case of receiving funds right before the recent blocks in weight proof
59
-- [full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
54
++ @pytest.mark.limit_consensus_modes(reason="save time")
55
++ @pytest.mark.anyio
56
++ async def test_request_block_headers(simulator_and_wallet, default_1000_blocks):
57
++ # Tests the edge case of receiving funds right before the recent blocks in weight proof
58
++ [full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
59
++
60
++ wallet = wallet_node.wallet_state_manager.main_wallet
61
++ ph = await wallet.get_new_puzzlehash()
62
++ for block in default_1000_blocks[:100]:
63
++ await full_node_api.full_node.add_block(block)
64
++
65
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(15), False))
66
++ assert msg.type == ProtocolMessageTypes.respond_block_headers.value
67
++ res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
68
++ bh = res_block_headers.header_blocks
69
++ assert len(bh) == 6
70
++ assert [x.reward_chain_block.height for x in default_1000_blocks[10:16]] == [
71
++ x.reward_chain_block.height for x in bh
72
++ ]
73
++
74
++ assert [x.foliage for x in default_1000_blocks[10:16]] == [x.foliage for x in bh]
75
++
76
++ assert [x.transactions_filter for x in bh] == [b"\x00"] * 6
77
++
78
++ num_blocks = 20
79
++ new_blocks = bt.get_consecutive_blocks(num_blocks, block_list_input=default_1000_blocks, pool_reward_puzzle_hash=ph)
80
++ for i in range(0, len(new_blocks)):
81
++ await full_node_api.full_node.add_block(new_blocks[i])
82
++
83
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(110), uint32(115), True))
84
++ res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
85
++ bh = res_block_headers.header_blocks
86
++ assert len(bh) == 6
87
++
88
++
89
++ # @pytest.mark.parametrize(
90
++ # "test_case",
91
++ # [(1000000, 10000010, False, ProtocolMessageTypes.reject_block_headers)],
92
++ # [(80, 99, False, ProtocolMessageTypes.respond_block_headers)],
93
++ # [(10, 8, False, None)],
94
++ # )
95
++ @pytest.mark.anyio
96
++ async def test_request_block_headers_rejected(simulator_and_wallet, default_1000_blocks):
97
++ # Tests the edge case of receiving funds right before the recent blocks in weight proof
98
++ [full_node_api], _, bt = simulator_and_wallet
99
++
100
++ # start_height, end_height, return_filter, expected_res = test_case
101
++
102
++ msg = await full_node_api.request_block_headers(
103
++ wallet_protocol.RequestBlockHeaders(uint32(1000000), uint32(1000010), False)
104
++ )
105
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
106
++
107
++ for block in default_1000_blocks[:150]:
108
++ await full_node_api.full_node.add_block(block)
109
++
110
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(80), uint32(99), False))
111
++ assert msg.type == ProtocolMessageTypes.respond_block_headers.value
112
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), False))
113
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
114
++
115
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), True))
116
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
117
++
118
++ # test for 128 blocks to fetch at once limit
119
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(140), True))
120
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
121
++
122
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), False))
123
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
124
++ msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), True))
125
++ assert msg.type == ProtocolMessageTypes.reject_block_headers.value
126
++
127
++
128
++ @pytest.mark.parametrize(
129
++ "two_wallet_nodes",
130
++ [
131
++ dict(
132
++ disable_capabilities=[Capability.BLOCK_HEADERS],
133
++ ),
134
++ dict(
135
++ disable_capabilities=[Capability.BASE],
136
++ ),
137
++ ],
138
++ indirect=True,
139
++ )
140
++ @pytest.mark.limit_consensus_modes(reason="save time")
141
++ @pytest.mark.anyio
142
++ async def test_basic_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
143
++ full_nodes, wallets, bt = two_wallet_nodes
144
++ full_node_api = full_nodes[0]
145
++ full_node_server = full_node_api.full_node.server
146
++
147
++ # Trusted node sync
148
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
149
++
150
++ # Untrusted node sync
151
++ wallets[1][0].config["trusted_peers"] = {}
152
++
153
++ for block in default_400_blocks:
154
++ await full_node_api.full_node.add_block(block)
155
++
156
++ for wallet_node, wallet_server in wallets:
157
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
60
158

61
-- wallet = wallet_node.wallet_state_manager.main_wallet
62
-- ph = await wallet.get_new_puzzlehash()
63
-- for block in default_1000_blocks[:100]:
64
-- await full_node_api.full_node.add_block(block)
159
++ for wallet_node, wallet_server in wallets:
160
++ await time_out_assert(100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
65
161

66
-- msg = await full_node_api.request_block_headers(
67
-- wallet_protocol.RequestBlockHeaders(uint32(10), uint32(15), False)
162
++ # Tests a reorg with the wallet
163
++ num_blocks = 30
164
++ blocks_reorg = bt.get_consecutive_blocks(num_blocks - 1, block_list_input=default_400_blocks[:-5])
165
++ blocks_reorg = bt.get_consecutive_blocks(1, blocks_reorg, guarantee_transaction_block=True, current_time=True)
166
++ for i in range(1, len(blocks_reorg)):
167
++ await full_node_api.full_node.add_block(blocks_reorg[i])
168
++
169
++ for wallet_node, wallet_server in wallets:
170
++ await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
171
++
172
++ for wallet_node, wallet_server in wallets:
173
++ await time_out_assert(
174
++ 100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) + num_blocks - 5 - 1
68
175
)
69
-- assert msg.type == ProtocolMessageTypes.respond_block_headers.value
70
-- res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
71
-- bh = res_block_headers.header_blocks
72
-- assert len(bh) == 6
73
-- assert [x.reward_chain_block.height for x in default_1000_blocks[10:16]] == [
74
-- x.reward_chain_block.height for x in bh
75
-- ]
76
--
77
-- assert [x.foliage for x in default_1000_blocks[10:16]] == [x.foliage for x in bh]
176
++ await time_out_assert(20, wallet_node.wallet_state_manager.synced)
177
++ await disconnect_all(wallet_server)
178
++ assert not (await wallet_node.wallet_state_manager.synced())
179
++
180
++
181
++ @pytest.mark.parametrize(
182
++ "two_wallet_nodes",
183
++ [
184
++ dict(
185
++ disable_capabilities=[Capability.BLOCK_HEADERS],
186
++ ),
187
++ dict(
188
++ disable_capabilities=[Capability.BASE],
189
++ ),
190
++ ],
191
++ indirect=True,
192
++ )
193
++ @pytest.mark.limit_consensus_modes(reason="save time")
194
++ @pytest.mark.anyio
195
++ async def test_almost_recent(two_wallet_nodes, default_400_blocks, self_hostname, blockchain_constants):
196
++ # Tests the edge case of receiving funds right before the recent blocks in weight proof
197
++ full_nodes, wallets, bt = two_wallet_nodes
198
++ full_node_api = full_nodes[0]
199
++ full_node_server = full_node_api.full_node.server
200
++
201
++ # Trusted node sync
202
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
203
++
204
++ # Untrusted node sync
205
++ wallets[1][0].config["trusted_peers"] = {}
206
++
207
++ base_num_blocks = 400
208
++ for block in default_400_blocks:
209
++ await full_node_api.full_node.add_block(block)
210
++ all_blocks = default_400_blocks
211
++ both_phs = []
212
++ for wallet_node, wallet_server in wallets:
213
++ wallet = wallet_node.wallet_state_manager.main_wallet
214
++ both_phs.append(await wallet.get_new_puzzlehash())
78
215

79
-- assert [x.transactions_filter for x in bh] == [b"\x00"] * 6
216
++ for i in range(20):
217
++ # Tests a reorg with the wallet
218
++ ph = both_phs[i % 2]
219
++ all_blocks = bt.get_consecutive_blocks(1, block_list_input=all_blocks, pool_reward_puzzle_hash=ph)
220
++ await full_node_api.full_node.add_block(all_blocks[-1])
80
221

81
-- num_blocks = 20
82
-- new_blocks = bt.get_consecutive_blocks(
83
-- num_blocks, block_list_input=default_1000_blocks, pool_reward_puzzle_hash=ph
84
-- )
85
-- for i in range(0, len(new_blocks)):
86
-- await full_node_api.full_node.add_block(new_blocks[i])
222
++ new_blocks = bt.get_consecutive_blocks(
223
++ blockchain_constants.WEIGHT_PROOF_RECENT_BLOCKS + 10, block_list_input=all_blocks
224
++ )
225
++ for i in range(base_num_blocks + 20, len(new_blocks)):
226
++ await full_node_api.full_node.add_block(new_blocks[i])
87
227

88
-- msg = await full_node_api.request_block_headers(
89
-- wallet_protocol.RequestBlockHeaders(uint32(110), uint32(115), True)
90
-- )
91
-- res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
92
-- bh = res_block_headers.header_blocks
93
-- assert len(bh) == 6
94
--
95
-- # @pytest.mark.parametrize(
96
-- # "test_case",
97
-- # [(1000000, 10000010, False, ProtocolMessageTypes.reject_block_headers)],
98
-- # [(80, 99, False, ProtocolMessageTypes.respond_block_headers)],
99
-- # [(10, 8, False, None)],
100
-- # )
101
-- @pytest.mark.anyio
102
-- async def test_request_block_headers_rejected(self, simulator_and_wallet, default_1000_blocks):
103
-- # Tests the edge case of receiving funds right before the recent blocks in weight proof
104
-- [full_node_api], _, bt = simulator_and_wallet
105
--
106
-- # start_height, end_height, return_filter, expected_res = test_case
107
--
108
-- msg = await full_node_api.request_block_headers(
109
-- wallet_protocol.RequestBlockHeaders(uint32(1000000), uint32(1000010), False)
110
-- )
111
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
228
++ for wallet_node, wallet_server in wallets:
229
++ wallet = wallet_node.wallet_state_manager.main_wallet
230
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
231
++ await time_out_assert(30, wallet.get_confirmed_balance, 10 * calculate_pool_reward(uint32(1000)))
112
232

113
-- for block in default_1000_blocks[:150]:
114
-- await full_node_api.full_node.add_block(block)
115
233

116
-- msg = await full_node_api.request_block_headers(
117
-- wallet_protocol.RequestBlockHeaders(uint32(80), uint32(99), False)
118
-- )
119
-- assert msg.type == ProtocolMessageTypes.respond_block_headers.value
120
-- msg = await full_node_api.request_block_headers(
121
-- wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), False)
122
-- )
123
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
234
++ @pytest.mark.anyio
235
++ async def test_backtrack_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
236
++ full_nodes, wallets, _ = two_wallet_nodes
237
++ full_node_api = full_nodes[0]
238
++ full_node_server = full_node_api.full_node.server
124
239

125
-- msg = await full_node_api.request_block_headers(
126
-- wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), True)
127
-- )
128
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
240
++ # Trusted node sync
241
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
129
242

130
-- # test for 128 blocks to fetch at once limit
131
-- msg = await full_node_api.request_block_headers(
132
-- wallet_protocol.RequestBlockHeaders(uint32(10), uint32(140), True)
133
-- )
134
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
243
++ # Untrusted node sync
244
++ wallets[1][0].config["trusted_peers"] = {}
135
245

136
-- msg = await full_node_api.request_block_headers(
137
-- wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), False)
138
-- )
139
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
140
-- msg = await full_node_api.request_block_headers(
141
-- wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), True)
142
-- )
143
-- assert msg.type == ProtocolMessageTypes.reject_block_headers.value
246
++ for block in default_400_blocks[:20]:
247
++ await full_node_api.full_node.add_block(block)
144
248

145
-- @pytest.mark.parametrize(
146
-- "two_wallet_nodes",
147
-- [
148
-- dict(
149
-- disable_capabilities=[Capability.BLOCK_HEADERS],
150
-- ),
151
-- dict(
152
-- disable_capabilities=[Capability.BASE],
153
-- ),
154
-- ],
155
-- indirect=True,
156
-- )
157
-- @pytest.mark.limit_consensus_modes(reason="save time")
158
-- @pytest.mark.anyio
159
-- async def test_basic_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
160
-- full_nodes, wallets, bt = two_wallet_nodes
161
-- full_node_api = full_nodes[0]
162
-- full_node_server = full_node_api.full_node.server
249
++ for wallet_node, wallet_server in wallets:
250
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
163
251

164
-- # Trusted node sync
165
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
252
++ for wallet_node, wallet_server in wallets:
253
++ await time_out_assert(100, wallet_height_at_least, True, wallet_node, 19)
166
254

167
-- # Untrusted node sync
168
-- wallets[1][0].config["trusted_peers"] = {}
169
255

170
-- for block in default_400_blocks:
171
-- await full_node_api.full_node.add_block(block)
256
++ # Tests a reorg with the wallet
257
++ @pytest.mark.anyio
258
++ async def test_short_batch_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
259
++ full_nodes, wallets, _ = two_wallet_nodes
260
++ full_node_api = full_nodes[0]
261
++ full_node_server = full_node_api.full_node.server
172
262

173
-- for wallet_node, wallet_server in wallets:
174
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
263
++ # Trusted node sync
264
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
175
265

176
-- for wallet_node, wallet_server in wallets:
177
-- await time_out_assert(100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
266
++ # Untrusted node sync
267
++ wallets[1][0].config["trusted_peers"] = {}
178
268

179
-- # Tests a reorg with the wallet
180
-- num_blocks = 30
181
-- blocks_reorg = bt.get_consecutive_blocks(num_blocks - 1, block_list_input=default_400_blocks[:-5])
182
-- blocks_reorg = bt.get_consecutive_blocks(1, blocks_reorg, guarantee_transaction_block=True, current_time=True)
183
-- for i in range(1, len(blocks_reorg)):
184
-- await full_node_api.full_node.add_block(blocks_reorg[i])
185
--
186
-- for wallet_node, wallet_server in wallets:
187
-- await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
188
--
189
-- for wallet_node, wallet_server in wallets:
190
-- await time_out_assert(
191
-- 100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) + num_blocks - 5 - 1
192
-- )
193
-- await time_out_assert(20, wallet_node.wallet_state_manager.synced)
194
-- await disconnect_all(wallet_server)
195
-- assert not (await wallet_node.wallet_state_manager.synced())
269
++ for block in default_400_blocks[:200]:
270
++ await full_node_api.full_node.add_block(block)
196
271

197
-- @pytest.mark.parametrize(
198
-- "two_wallet_nodes",
199
-- [
200
-- dict(
201
-- disable_capabilities=[Capability.BLOCK_HEADERS],
202
-- ),
203
-- dict(
204
-- disable_capabilities=[Capability.BASE],
205
-- ),
206
-- ],
207
-- indirect=True,
208
-- )
209
-- @pytest.mark.limit_consensus_modes(reason="save time")
210
-- @pytest.mark.anyio
211
-- async def test_almost_recent(self, two_wallet_nodes, default_400_blocks, self_hostname, blockchain_constants):
212
-- # Tests the edge case of receiving funds right before the recent blocks in weight proof
213
-- full_nodes, wallets, bt = two_wallet_nodes
214
-- full_node_api = full_nodes[0]
215
-- full_node_server = full_node_api.full_node.server
216
--
217
-- # Trusted node sync
218
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
219
--
220
-- # Untrusted node sync
221
-- wallets[1][0].config["trusted_peers"] = {}
222
--
223
-- base_num_blocks = 400
224
-- for block in default_400_blocks:
225
-- await full_node_api.full_node.add_block(block)
226
-- all_blocks = default_400_blocks
227
-- both_phs = []
228
-- for wallet_node, wallet_server in wallets:
229
-- wallet = wallet_node.wallet_state_manager.main_wallet
230
-- both_phs.append(await wallet.get_new_puzzlehash())
231
--
232
-- for i in range(20):
233
-- # Tests a reorg with the wallet
234
-- ph = both_phs[i % 2]
235
-- all_blocks = bt.get_consecutive_blocks(1, block_list_input=all_blocks, pool_reward_puzzle_hash=ph)
236
-- await full_node_api.full_node.add_block(all_blocks[-1])
237
--
238
-- new_blocks = bt.get_consecutive_blocks(
239
-- blockchain_constants.WEIGHT_PROOF_RECENT_BLOCKS + 10, block_list_input=all_blocks
240
-- )
241
-- for i in range(base_num_blocks + 20, len(new_blocks)):
242
-- await full_node_api.full_node.add_block(new_blocks[i])
272
++ for wallet_node, wallet_server in wallets:
273
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
243
274

244
-- for wallet_node, wallet_server in wallets:
245
-- wallet = wallet_node.wallet_state_manager.main_wallet
246
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
247
-- await time_out_assert(30, wallet.get_confirmed_balance, 10 * calculate_pool_reward(uint32(1000)))
275
++ for wallet_node, wallet_server in wallets:
276
++ await time_out_assert(100, wallet_height_at_least, True, wallet_node, 199)
248
277

249
-- @pytest.mark.anyio
250
-- async def test_backtrack_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
251
-- full_nodes, wallets, _ = two_wallet_nodes
252
-- full_node_api = full_nodes[0]
253
-- full_node_server = full_node_api.full_node.server
254
278

255
-- # Trusted node sync
256
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
279
++ @pytest.mark.limit_consensus_modes(reason="save time")
280
++ @pytest.mark.anyio
281
++ async def test_long_sync_wallet(two_wallet_nodes, default_1000_blocks, default_400_blocks, self_hostname):
282
++ full_nodes, wallets, bt = two_wallet_nodes
283
++ full_node_api = full_nodes[0]
284
++ full_node_server = full_node_api.full_node.server
257
285

258
-- # Untrusted node sync
259
-- wallets[1][0].config["trusted_peers"] = {}
286
++ # Trusted node sync
287
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
260
288

261
-- for block in default_400_blocks[:20]:
262
-- await full_node_api.full_node.add_block(block)
289
++ # Untrusted node sync
290
++ wallets[1][0].config["trusted_peers"] = {}
263
291

264
-- for wallet_node, wallet_server in wallets:
265
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
292
++ for block in default_400_blocks:
293
++ await full_node_api.full_node.add_block(block)
266
294

267
-- for wallet_node, wallet_server in wallets:
268
-- await time_out_assert(100, wallet_height_at_least, True, wallet_node, 19)
295
++ for wallet_node, wallet_server in wallets:
296
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
269
297

270
-- # Tests a reorg with the wallet
271
-- @pytest.mark.anyio
272
-- async def test_short_batch_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
273
-- full_nodes, wallets, _ = two_wallet_nodes
274
-- full_node_api = full_nodes[0]
275
-- full_node_server = full_node_api.full_node.server
298
++ for wallet_node, wallet_server in wallets:
299
++ await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
276
300

277
-- # Trusted node sync
278
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
301
++ # Tests a long reorg
302
++ for block in default_1000_blocks:
303
++ await full_node_api.full_node.add_block(block)
279
304

280
-- # Untrusted node sync
281
-- wallets[1][0].config["trusted_peers"] = {}
305
++ for wallet_node, wallet_server in wallets:
306
++ await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
282
307

283
-- for block in default_400_blocks[:200]:
284
-- await full_node_api.full_node.add_block(block)
308
++ log.info(f"wallet node height is {await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()}")
309
++ await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) - 1)
285
310

286
-- for wallet_node, wallet_server in wallets:
287
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
311
++ await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
288
312

289
-- for wallet_node, wallet_server in wallets:
290
-- await time_out_assert(100, wallet_height_at_least, True, wallet_node, 199)
313
++ # Tests a short reorg
314
++ num_blocks = 30
315
++ blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_1000_blocks[:-5])
291
316

292
-- @pytest.mark.limit_consensus_modes(reason="save time")
293
-- @pytest.mark.anyio
294
-- async def test_long_sync_wallet(self, two_wallet_nodes, default_1000_blocks, default_400_blocks, self_hostname):
295
-- full_nodes, wallets, bt = two_wallet_nodes
296
-- full_node_api = full_nodes[0]
297
-- full_node_server = full_node_api.full_node.server
317
++ for i in range(len(blocks_reorg) - num_blocks - 10, len(blocks_reorg)):
318
++ await full_node_api.full_node.add_block(blocks_reorg[i])
298
319

299
-- # Trusted node sync
300
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
320
++ for wallet_node, wallet_server in wallets:
321
++ await time_out_assert(
322
++ 600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) + num_blocks - 5 - 1
323
++ )
301
324

302
-- # Untrusted node sync
303
-- wallets[1][0].config["trusted_peers"] = {}
304
325

305
-- for block in default_400_blocks:
306
-- await full_node_api.full_node.add_block(block)
326
++ @pytest.mark.limit_consensus_modes(reason="save time")
327
++ @pytest.mark.anyio
328
++ async def test_wallet_reorg_sync(two_wallet_nodes, default_400_blocks, self_hostname):
329
++ num_blocks = 5
330
++ full_nodes, wallets, bt = two_wallet_nodes
331
++ full_node_api = full_nodes[0]
332
++ full_node_server = full_node_api.full_node.server
307
333

308
-- for wallet_node, wallet_server in wallets:
309
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
334
++ # Trusted node sync
335
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
310
336

311
-- for wallet_node, wallet_server in wallets:
312
-- await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
337
++ # Untrusted node sync
338
++ wallets[1][0].config["trusted_peers"] = {}
313
339

314
-- # Tests a long reorg
315
-- for block in default_1000_blocks:
316
-- await full_node_api.full_node.add_block(block)
340
++ phs = []
341
++ for wallet_node, wallet_server in wallets:
342
++ wallet = wallet_node.wallet_state_manager.main_wallet
343
++ phs.append(await wallet.get_new_puzzlehash())
344
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
317
345

318
-- for wallet_node, wallet_server in wallets:
319
-- await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
346
++ # Insert 400 blocks
347
++ for block in default_400_blocks:
348
++ await full_node_api.full_node.add_block(block)
320
349

321
-- log.info(
322
-- f"wallet node height is {await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()}"
323
-- )
324
-- await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) - 1)
350
++ # Farm few more with reward
351
++ for i in range(0, num_blocks - 1):
352
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[0]))
325
353

326
-- await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
354
++ for i in range(0, num_blocks):
355
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[1]))
327
356

328
-- # Tests a short reorg
329
-- num_blocks = 30
330
-- blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_1000_blocks[:-5])
357
++ # Confirm we have the funds
358
++ funds = sum(
359
++ [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
360
++ )
331
361

332
-- for i in range(len(blocks_reorg) - num_blocks - 10, len(blocks_reorg)):
333
-- await full_node_api.full_node.add_block(blocks_reorg[i])
362
++ for wallet_node, wallet_server in wallets:
363
++ wallet = wallet_node.wallet_state_manager.main_wallet
364
++ await time_out_assert(60, wallet.get_confirmed_balance, funds)
365
++ await time_out_assert(60, get_tx_count, 2 * (num_blocks - 1), wallet_node.wallet_state_manager, 1)
334
366

335
-- for wallet_node, wallet_server in wallets:
336
-- await time_out_assert(
337
-- 600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) + num_blocks - 5 - 1
338
-- )
367
++ # Reorg blocks that carry reward
368
++ num_blocks = 30
369
++ blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_400_blocks[:-5])
339
370

340
-- @pytest.mark.limit_consensus_modes(reason="save time")
341
-- @pytest.mark.anyio
342
-- async def test_wallet_reorg_sync(self, two_wallet_nodes, default_400_blocks, self_hostname):
343
-- num_blocks = 5
344
-- full_nodes, wallets, bt = two_wallet_nodes
345
-- full_node_api = full_nodes[0]
346
-- full_node_server = full_node_api.full_node.server
371
++ for block in blocks_reorg[-30:]:
372
++ await full_node_api.full_node.add_block(block)
347
373

348
-- # Trusted node sync
349
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
374
++ for wallet_node, wallet_server in wallets:
375
++ wallet = wallet_node.wallet_state_manager.main_wallet
376
++ await time_out_assert(60, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
377
++ await time_out_assert(60, wallet.get_confirmed_balance, 0)
350
378

351
-- # Untrusted node sync
352
-- wallets[1][0].config["trusted_peers"] = {}
353
379

354
-- phs = []
355
-- for wallet_node, wallet_server in wallets:
356
-- wallet = wallet_node.wallet_state_manager.main_wallet
357
-- phs.append(await wallet.get_new_puzzlehash())
358
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
380
++ @pytest.mark.limit_consensus_modes(reason="save time")
381
++ @pytest.mark.anyio
382
++ async def test_wallet_reorg_get_coinbase(two_wallet_nodes, default_400_blocks, self_hostname):
383
++ full_nodes, wallets, bt = two_wallet_nodes
384
++ full_node_api = full_nodes[0]
385
++ full_node_server = full_node_api.full_node.server
359
386

360
-- # Insert 400 blocks
361
-- for block in default_400_blocks:
362
-- await full_node_api.full_node.add_block(block)
387
++ # Trusted node sync
388
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
363
389

364
-- # Farm few more with reward
365
-- for i in range(0, num_blocks - 1):
366
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[0]))
390
++ # Untrusted node sync
391
++ wallets[1][0].config["trusted_peers"] = {}
367
392

368
-- for i in range(0, num_blocks):
369
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[1]))
393
++ for wallet_node, wallet_server in wallets:
394
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
370
395

371
-- # Confirm we have the funds
372
-- funds = sum(
373
-- [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
374
-- )
396
++ # Insert 400 blocks
397
++ for block in default_400_blocks:
398
++ await full_node_api.full_node.add_block(block)
375
399

376
-- async def get_tx_count(wsm, wallet_id):
377
-- txs = await wsm.get_all_transactions(wallet_id)
378
-- return len(txs)
379
--
380
-- for wallet_node, wallet_server in wallets:
381
-- wallet = wallet_node.wallet_state_manager.main_wallet
382
-- await time_out_assert(60, wallet.get_confirmed_balance, funds)
383
-- await time_out_assert(60, get_tx_count, 2 * (num_blocks - 1), wallet_node.wallet_state_manager, 1)
384
--
385
-- # Reorg blocks that carry reward
386
-- num_blocks = 30
387
-- blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_400_blocks[:-5])
388
--
389
-- for block in blocks_reorg[-30:]:
390
-- await full_node_api.full_node.add_block(block)
391
--
392
-- for wallet_node, wallet_server in wallets:
393
-- wallet = wallet_node.wallet_state_manager.main_wallet
394
-- await time_out_assert(60, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
395
-- await time_out_assert(60, wallet.get_confirmed_balance, 0)
396
--
397
-- @pytest.mark.limit_consensus_modes(reason="save time")
398
-- @pytest.mark.anyio
399
-- async def test_wallet_reorg_get_coinbase(self, two_wallet_nodes, default_400_blocks, self_hostname):
400
-- full_nodes, wallets, bt = two_wallet_nodes
401
-- full_node_api = full_nodes[0]
402
-- full_node_server = full_node_api.full_node.server
403
--
404
-- # Trusted node sync
405
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
406
--
407
-- # Untrusted node sync
408
-- wallets[1][0].config["trusted_peers"] = {}
409
--
410
-- for wallet_node, wallet_server in wallets:
411
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
412
--
413
-- # Insert 400 blocks
414
-- for block in default_400_blocks:
415
-- await full_node_api.full_node.add_block(block)
416
--
417
-- # Reorg blocks that carry reward
418
-- num_blocks_reorg = 30
419
-- blocks_reorg = bt.get_consecutive_blocks(num_blocks_reorg, block_list_input=default_400_blocks[:-5])
420
--
421
-- for block in blocks_reorg[:-5]:
422
-- await full_node_api.full_node.add_block(block)
423
--
424
-- async def get_tx_count(wsm, wallet_id):
425
-- txs = await wsm.get_all_transactions(wallet_id)
426
-- return len(txs)
427
--
428
-- for wallet_node, wallet_server in wallets:
429
-- await time_out_assert(30, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
430
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
431
--
432
-- num_blocks_reorg_1 = 40
433
-- all_blocks_reorg_2 = blocks_reorg[:-30]
434
-- for wallet_node, wallet_server in wallets:
435
-- wallet = wallet_node.wallet_state_manager.main_wallet
436
-- ph = await wallet.get_new_puzzlehash()
437
-- all_blocks_reorg_2 = bt.get_consecutive_blocks(
438
-- 1, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph, block_list_input=all_blocks_reorg_2
439
-- )
440
-- blocks_reorg_2 = bt.get_consecutive_blocks(num_blocks_reorg_1, block_list_input=all_blocks_reorg_2)
400
++ # Reorg blocks that carry reward
401
++ num_blocks_reorg = 30
402
++ blocks_reorg = bt.get_consecutive_blocks(num_blocks_reorg, block_list_input=default_400_blocks[:-5])
441
403

442
-- for block in blocks_reorg_2[-44:]:
443
-- await full_node_api.full_node.add_block(block)
404
++ for block in blocks_reorg[:-5]:
405
++ await full_node_api.full_node.add_block(block)
444
406

445
-- for wallet_node, wallet_server in wallets:
446
-- await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
407
++ for wallet_node, wallet_server in wallets:
408
++ await time_out_assert(30, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
409
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
447
410

448
-- # Confirm we have the funds
449
-- funds = calculate_pool_reward(uint32(len(all_blocks_reorg_2))) + calculate_base_farmer_reward(
450
-- uint32(len(all_blocks_reorg_2))
411
++ num_blocks_reorg_1 = 40
412
++ all_blocks_reorg_2 = blocks_reorg[:-30]
413
++ for wallet_node, wallet_server in wallets:
414
++ wallet = wallet_node.wallet_state_manager.main_wallet
415
++ ph = await wallet.get_new_puzzlehash()
416
++ all_blocks_reorg_2 = bt.get_consecutive_blocks(
417
++ 1, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph, block_list_input=all_blocks_reorg_2
451
418
)
419
++ blocks_reorg_2 = bt.get_consecutive_blocks(num_blocks_reorg_1, block_list_input=all_blocks_reorg_2)
452
420

453
-- for wallet_node, wallet_server in wallets:
454
-- wallet = wallet_node.wallet_state_manager.main_wallet
455
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=60)
421
++ for block in blocks_reorg_2[-44:]:
422
++ await full_node_api.full_node.add_block(block)
456
423

457
-- await time_out_assert(20, get_tx_count, 2, wallet_node.wallet_state_manager, 1)
458
-- await time_out_assert(20, wallet.get_confirmed_balance, funds)
424
++ for wallet_node, wallet_server in wallets:
425
++ await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
459
426

460
-- @pytest.mark.anyio
461
-- async def test_request_additions_errors(self, simulator_and_wallet, self_hostname):
462
-- full_nodes, wallets, _ = simulator_and_wallet
463
-- wallet_node, wallet_server = wallets[0]
427
++ # Confirm we have the funds
428
++ funds = calculate_pool_reward(uint32(len(all_blocks_reorg_2))) + calculate_base_farmer_reward(
429
++ uint32(len(all_blocks_reorg_2))
430
++ )
431
++
432
++ for wallet_node, wallet_server in wallets:
464
433
wallet = wallet_node.wallet_state_manager.main_wallet
465
-- ph = await wallet.get_new_puzzlehash()
434
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=60)
466
435

467
-- full_node_api = full_nodes[0]
468
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
436
++ await time_out_assert(20, get_tx_count, 2, wallet_node.wallet_state_manager, 1)
437
++ await time_out_assert(20, wallet.get_confirmed_balance, funds)
469
438

470
-- for i in range(2):
471
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
472
439

473
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
440
++ @pytest.mark.anyio
441
++ async def test_request_additions_errors(simulator_and_wallet, self_hostname):
442
++ full_nodes, wallets, _ = simulator_and_wallet
443
++ wallet_node, wallet_server = wallets[0]
444
++ wallet = wallet_node.wallet_state_manager.main_wallet
445
++ ph = await wallet.get_new_puzzlehash()
474
446

475
-- last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
476
-- assert last_block is not None
447
++ full_node_api = full_nodes[0]
448
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
477
449

478
-- # Invalid height
479
-- with pytest.raises(ValueError):
480
-- await full_node_api.request_additions(RequestAdditions(uint64(100), last_block.header_hash, [ph]))
450
++ for i in range(2):
451
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
481
452

482
-- # Invalid header hash
483
-- with pytest.raises(ValueError):
484
-- await full_node_api.request_additions(RequestAdditions(last_block.height, std_hash(b""), [ph]))
453
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
485
454

486
-- # No results
487
-- res1: Optional[Message] = await full_node_api.request_additions(
488
-- RequestAdditions(last_block.height, last_block.header_hash, [std_hash(b"")])
489
-- )
490
-- assert res1 is not None
491
-- response = RespondAdditions.from_bytes(res1.data)
492
-- assert response.height == last_block.height
493
-- assert response.header_hash == last_block.header_hash
494
-- assert len(response.proofs) == 1
495
-- assert len(response.coins) == 1
496
--
497
-- assert response.proofs[0][0] == std_hash(b"")
498
-- assert response.proofs[0][1] is not None
499
-- assert response.proofs[0][2] is None
500
--
501
-- @pytest.mark.anyio
502
-- async def test_request_additions_success(self, simulator_and_wallet, self_hostname):
503
-- full_nodes, wallets, _ = simulator_and_wallet
504
-- wallet_node, wallet_server = wallets[0]
505
-- wallet = wallet_node.wallet_state_manager.main_wallet
506
-- ph = await wallet.get_new_puzzlehash()
455
++ last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
456
++ assert last_block is not None
507
457

508
-- full_node_api = full_nodes[0]
509
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
458
++ # Invalid height
459
++ with pytest.raises(ValueError):
460
++ await full_node_api.request_additions(RequestAdditions(uint64(100), last_block.header_hash, [ph]))
510
461

511
-- for i in range(2):
512
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
462
++ # Invalid header hash
463
++ with pytest.raises(ValueError):
464
++ await full_node_api.request_additions(RequestAdditions(last_block.height, std_hash(b""), [ph]))
513
465

514
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
466
++ # No results
467
++ res1: Optional[Message] = await full_node_api.request_additions(
468
++ RequestAdditions(last_block.height, last_block.header_hash, [std_hash(b"")])
469
++ )
470
++ assert res1 is not None
471
++ response = RespondAdditions.from_bytes(res1.data)
472
++ assert response.height == last_block.height
473
++ assert response.header_hash == last_block.header_hash
474
++ assert len(response.proofs) == 1
475
++ assert len(response.coins) == 1
515
476

516
-- payees: List[Payment] = []
517
-- for i in range(10):
518
-- payee_ph = await wallet.get_new_puzzlehash()
519
-- payees.append(Payment(payee_ph, uint64(i + 100)))
520
-- payees.append(Payment(payee_ph, uint64(i + 200)))
477
++ assert response.proofs[0][0] == std_hash(b"")
478
++ assert response.proofs[0][1] is not None
479
++ assert response.proofs[0][2] is None
521
480

522
-- [tx] = await wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
523
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
524
481

525
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
482
++ @pytest.mark.anyio
483
++ async def test_request_additions_success(simulator_and_wallet, self_hostname):
484
++ full_nodes, wallets, _ = simulator_and_wallet
485
++ wallet_node, wallet_server = wallets[0]
486
++ wallet = wallet_node.wallet_state_manager.main_wallet
487
++ ph = await wallet.get_new_puzzlehash()
526
488

527
-- last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
528
-- assert last_block is not None
529
-- await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
489
++ full_node_api = full_nodes[0]
490
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
530
491

531
-- res2: Optional[Message] = await full_node_api.request_additions(
532
-- RequestAdditions(
533
-- last_block.height,
534
-- None,
535
-- [payees[0].puzzle_hash, payees[2].puzzle_hash, std_hash(b"1")],
536
-- )
537
-- )
492
++ for i in range(2):
493
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
538
494

539
-- assert res2 is not None
540
-- response = RespondAdditions.from_bytes(res2.data)
541
-- assert response.height == last_block.height
542
-- assert response.header_hash == last_block.header_hash
543
-- assert len(response.proofs) == 3
495
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
544
496

545
-- # First two PHs are included
546
-- for i in range(2):
547
-- assert response.proofs[i][0] in {payees[j].puzzle_hash for j in (0, 2)}
548
-- assert response.proofs[i][1] is not None
549
-- assert response.proofs[i][2] is not None
497
++ payees: List[Payment] = []
498
++ for i in range(10):
499
++ payee_ph = await wallet.get_new_puzzlehash()
500
++ payees.append(Payment(payee_ph, uint64(i + 100)))
501
++ payees.append(Payment(payee_ph, uint64(i + 200)))
550
502

551
-- # Third PH is not included
552
-- assert response.proofs[2][2] is None
503
++ [tx] = await wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
504
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
553
505

554
-- coin_list_dict = {p: coin_list for p, coin_list in response.coins}
506
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
555
507

556
-- assert len(coin_list_dict) == 3
557
-- for p, coin_list in coin_list_dict.items():
558
-- if p == std_hash(b"1"):
559
-- # this is the one that is not included
560
-- assert len(coin_list) == 0
561
-- else:
562
-- for coin in coin_list:
563
-- assert coin.puzzle_hash == p
564
-- # The other ones are included
565
-- assert len(coin_list) == 2
566
--
567
-- # None for puzzle hashes returns all coins and no proofs
568
-- res3: Optional[Message] = await full_node_api.request_additions(
569
-- RequestAdditions(last_block.height, last_block.header_hash, None)
570
-- )
508
++ last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
509
++ assert last_block is not None
510
++ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
571
511

572
-- assert res3 is not None
573
-- response = RespondAdditions.from_bytes(res3.data)
574
-- assert response.height == last_block.height
575
-- assert response.header_hash == last_block.header_hash
576
-- assert response.proofs is None
577
-- assert len(response.coins) == 12
578
-- assert sum([len(c_list) for _, c_list in response.coins]) == 24
579
--
580
-- # [] for puzzle hashes returns nothing
581
-- res4: Optional[Message] = await full_node_api.request_additions(
582
-- RequestAdditions(last_block.height, last_block.header_hash, [])
583
-- )
584
-- assert res4 is not None
585
-- response = RespondAdditions.from_bytes(res4.data)
586
-- assert response.proofs == []
587
-- assert len(response.coins) == 0
588
--
589
-- @pytest.mark.anyio
590
-- async def test_get_wp_fork_point(self, default_10000_blocks, blockchain_constants):
591
-- blocks = default_10000_blocks
592
-- header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
593
-- blocks, blockchain_constants
512
++ res2: Optional[Message] = await full_node_api.request_additions(
513
++ RequestAdditions(
514
++ last_block.height,
515
++ None,
516
++ [payees[0].puzzle_hash, payees[2].puzzle_hash, std_hash(b"1")],
594
517
)
595
-- wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
596
-- wp1 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9000)]].header_hash)
597
-- wp2 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9030)]].header_hash)
598
-- wp3 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(7500)]].header_hash)
599
-- wp4 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(8700)]].header_hash)
600
-- wp5 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9700)]].header_hash)
601
-- wp6 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9010)]].header_hash)
602
-- fork12 = get_wp_fork_point(blockchain_constants, wp1, wp2)
603
-- fork13 = get_wp_fork_point(blockchain_constants, wp3, wp1)
604
-- fork14 = get_wp_fork_point(blockchain_constants, wp4, wp1)
605
-- fork23 = get_wp_fork_point(blockchain_constants, wp3, wp2)
606
-- fork24 = get_wp_fork_point(blockchain_constants, wp4, wp2)
607
-- fork34 = get_wp_fork_point(blockchain_constants, wp3, wp4)
608
-- fork45 = get_wp_fork_point(blockchain_constants, wp4, wp5)
609
-- fork16 = get_wp_fork_point(blockchain_constants, wp1, wp6)
610
--
611
-- # overlap between recent chain in wps, fork point is the tip of the shorter wp
612
-- assert fork12 == wp1.recent_chain_data[-1].height
613
-- assert fork16 == wp1.recent_chain_data[-1].height
614
--
615
-- # if there is an overlap between the recent chains we can find the exact fork point
616
-- # if not we should get the latest block with a sub epoch summary that exists in both wp's
617
-- # this can happen in fork24 and fork14 since they are not very far and also not very close
618
--
619
-- if wp2.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
620
-- assert fork24 in summaries.keys()
621
-- assert fork24 < wp4.recent_chain_data[-1].height
622
-- else:
623
-- assert fork24 == wp4.recent_chain_data[-1].height
518
++ )
519
++
520
++ assert res2 is not None
521
++ response = RespondAdditions.from_bytes(res2.data)
522
++ assert response.height == last_block.height
523
++ assert response.header_hash == last_block.header_hash
524
++ assert len(response.proofs) == 3
525
++
526
++ # First two PHs are included
527
++ for i in range(2):
528
++ assert response.proofs[i][0] in {payees[j].puzzle_hash for j in (0, 2)}
529
++ assert response.proofs[i][1] is not None
530
++ assert response.proofs[i][2] is not None
531
++
532
++ # Third PH is not included
533
++ assert response.proofs[2][2] is None
624
534

625
-- if wp1.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
626
-- assert fork14 in summaries.keys()
627
-- assert fork14 < wp4.recent_chain_data[-1].height
535
++ coin_list_dict = {p: coin_list for p, coin_list in response.coins}
536
++
537
++ assert len(coin_list_dict) == 3
538
++ for p, coin_list in coin_list_dict.items():
539
++ if p == std_hash(b"1"):
540
++ # this is the one that is not included
541
++ assert len(coin_list) == 0
628
542
else:
629
-- assert fork14 == wp4.recent_chain_data[-1].height
630
--
631
-- # no overlap between recent chain in wps, fork point
632
-- # is the latest block with a sub epoch summary that exists in both wp's
633
-- assert fork13 in summaries.keys()
634
-- assert fork13 < wp3.recent_chain_data[-1].height
635
-- assert fork23 in summaries.keys()
636
-- assert fork23 < wp3.recent_chain_data[-1].height
637
-- assert fork34 in summaries.keys()
638
-- assert fork23 < wp3.recent_chain_data[-1].height
639
-- assert fork45 in summaries.keys()
640
-- assert fork45 < wp4.recent_chain_data[-1].height
641
--
642
-- """
643
-- This tests that a wallet filters out the dust properly.
644
-- It runs in seven phases:
645
-- 1. Create a single dust coin.
646
-- Typically (though there are edge cases), this coin will not be filtered.
647
-- 2. Create dust coins until the filter threshold has been reached.
648
-- At this point, none of the dust should be filtered.
649
-- 3. Create 10 coins that are exactly the size of the filter threshold.
650
-- These should not be filtered because they are not dust.
651
-- 4. Create one more dust coin. This coin should be filtered.
652
-- 5. Create 5 coins below the threshold and 5 at or above.
653
-- Those below the threshold should get filtered, and those above should not.
654
-- 6. Clear all coins from the dust wallet.
655
-- Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
656
-- Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
657
-- 7: Create an NFT wallet for the farmer wallet, and generate an NFT in that wallet.
658
-- Create an NFT wallet for the dust wallet.
659
-- Send the NFT to the dust wallet. The NFT should not be filtered.
660
-- """
661
--
662
-- @pytest.mark.anyio
663
-- @pytest.mark.parametrize(
664
-- "spam_filter_after_n_txs, xch_spam_amount, dust_value",
665
-- [
666
-- # In the following tests, the filter is run right away:
667
-- (0, 1, 1), # nothing is filtered
668
-- # In the following tests, 1 coin will be created in part 1, and 9 in part 2:
669
-- (10, 10000000000, 1), # everything is dust
670
-- (10, 10000000000, 10000000000), # max dust threshold, dust is same size so not filtered
671
-- # Test with more coins
672
-- (105, 1000000, 1), # default filter level (1m mojos), default dust size (1)
673
-- ],
543
++ for coin in coin_list:
544
++ assert coin.puzzle_hash == p
545
++ # The other ones are included
546
++ assert len(coin_list) == 2
547
++
548
++ # None for puzzle hashes returns all coins and no proofs
549
++ res3: Optional[Message] = await full_node_api.request_additions(
550
++ RequestAdditions(last_block.height, last_block.header_hash, None)
674
551
)
675
-- async def test_dusted_wallet(
676
-- self,
677
-- self_hostname,
678
-- two_wallet_nodes_custom_spam_filtering,
679
-- spam_filter_after_n_txs,
680
-- xch_spam_amount,
681
-- dust_value,
682
-- ):
683
-- full_nodes, wallets, _ = two_wallet_nodes_custom_spam_filtering
684
--
685
-- farm_wallet_node, farm_wallet_server = wallets[0]
686
-- dust_wallet_node, dust_wallet_server = wallets[1]
687
--
688
-- # Create two wallets, one for farming (not used for testing), and one for testing dust.
689
-- farm_wallet = farm_wallet_node.wallet_state_manager.main_wallet
690
-- dust_wallet = dust_wallet_node.wallet_state_manager.main_wallet
691
-- ph = await farm_wallet.get_new_puzzlehash()
692
--
693
-- full_node_api = full_nodes[0]
694
--
695
-- # It's also possible to obtain the current settings for spam_filter_after_n_txs and xch_spam_amount
696
-- # spam_filter_after_n_txs = wallets[0][0].config["spam_filter_after_n_txs"]
697
-- # xch_spam_amount = wallets[0][0].config["xch_spam_amount"]
698
-- # dust_value=1
699
--
700
-- # Verify legal values for the settings to be tested
701
-- # If spam_filter_after_n_txs is greater than 250, this test will take a long time to run.
702
-- # Current max value for xch_spam_amount is 0.01 XCH.
703
-- # If needed, this could be increased but we would need to farm more blocks.
704
-- # The max dust_value could be increased, but would require farming more blocks.
705
-- assert spam_filter_after_n_txs >= 0
706
-- assert spam_filter_after_n_txs <= 250
707
-- assert xch_spam_amount >= 1
708
-- assert xch_spam_amount <= 10000000000
709
-- assert dust_value >= 1
710
-- assert dust_value <= 10000000000
711
--
712
-- # start both clients
713
-- await farm_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
714
-- await dust_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
715
--
716
-- # Farm two blocks
717
-- for i in range(2):
718
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
719
552

720
-- # sync both nodes
721
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
553
++ assert res3 is not None
554
++ response = RespondAdditions.from_bytes(res3.data)
555
++ assert response.height == last_block.height
556
++ assert response.header_hash == last_block.header_hash
557
++ assert response.proofs is None
558
++ assert len(response.coins) == 12
559
++ assert sum([len(c_list) for _, c_list in response.coins]) == 24
560
++
561
++ # [] for puzzle hashes returns nothing
562
++ res4: Optional[Message] = await full_node_api.request_additions(
563
++ RequestAdditions(last_block.height, last_block.header_hash, [])
564
++ )
565
++ assert res4 is not None
566
++ response = RespondAdditions.from_bytes(res4.data)
567
++ assert response.proofs == []
568
++ assert len(response.coins) == 0
569
++
570
++
571
++ @pytest.mark.anyio
572
++ async def test_get_wp_fork_point(default_10000_blocks, blockchain_constants):
573
++ blocks = default_10000_blocks
574
++ header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks, blockchain_constants)
575
++ wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
576
++ wp1 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9000)]].header_hash)
577
++ wp2 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9030)]].header_hash)
578
++ wp3 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(7500)]].header_hash)
579
++ wp4 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(8700)]].header_hash)
580
++ wp5 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9700)]].header_hash)
581
++ wp6 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9010)]].header_hash)
582
++ fork12 = get_wp_fork_point(blockchain_constants, wp1, wp2)
583
++ fork13 = get_wp_fork_point(blockchain_constants, wp3, wp1)
584
++ fork14 = get_wp_fork_point(blockchain_constants, wp4, wp1)
585
++ fork23 = get_wp_fork_point(blockchain_constants, wp3, wp2)
586
++ fork24 = get_wp_fork_point(blockchain_constants, wp4, wp2)
587
++ fork34 = get_wp_fork_point(blockchain_constants, wp3, wp4)
588
++ fork45 = get_wp_fork_point(blockchain_constants, wp4, wp5)
589
++ fork16 = get_wp_fork_point(blockchain_constants, wp1, wp6)
590
++
591
++ # overlap between recent chain in wps, fork point is the tip of the shorter wp
592
++ assert fork12 == wp1.recent_chain_data[-1].height
593
++ assert fork16 == wp1.recent_chain_data[-1].height
594
++
595
++ # if there is an overlap between the recent chains we can find the exact fork point
596
++ # if not we should get the latest block with a sub epoch summary that exists in both wp's
597
++ # this can happen in fork24 and fork14 since they are not very far and also not very close
598
++
599
++ if wp2.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
600
++ assert fork24 in summaries.keys()
601
++ assert fork24 < wp4.recent_chain_data[-1].height
602
++ else:
603
++ assert fork24 == wp4.recent_chain_data[-1].height
604
++
605
++ if wp1.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
606
++ assert fork14 in summaries.keys()
607
++ assert fork14 < wp4.recent_chain_data[-1].height
608
++ else:
609
++ assert fork14 == wp4.recent_chain_data[-1].height
610
++
611
++ # no overlap between recent chain in wps, fork point
612
++ # is the latest block with a sub epoch summary that exists in both wp's
613
++ assert fork13 in summaries.keys()
614
++ assert fork13 < wp3.recent_chain_data[-1].height
615
++ assert fork23 in summaries.keys()
616
++ assert fork23 < wp3.recent_chain_data[-1].height
617
++ assert fork34 in summaries.keys()
618
++ assert fork23 < wp3.recent_chain_data[-1].height
619
++ assert fork45 in summaries.keys()
620
++ assert fork45 < wp4.recent_chain_data[-1].height
621
++
622
++
623
++ """
624
++ This tests that a wallet filters out the dust properly.
625
++ It runs in seven phases:
626
++ 1. Create a single dust coin.
627
++ Typically (though there are edge cases), this coin will not be filtered.
628
++ 2. Create dust coins until the filter threshold has been reached.
629
++ At this point, none of the dust should be filtered.
630
++ 3. Create 10 coins that are exactly the size of the filter threshold.
631
++ These should not be filtered because they are not dust.
632
++ 4. Create one more dust coin. This coin should be filtered.
633
++ 5. Create 5 coins below the threshold and 5 at or above.
634
++ Those below the threshold should get filtered, and those above should not.
635
++ 6. Clear all coins from the dust wallet.
636
++ Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
637
++ Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
638
++ 7: Create an NFT wallet for the farmer wallet, and generate an NFT in that wallet.
639
++ Create an NFT wallet for the dust wallet.
640
++ Send the NFT to the dust wallet. The NFT should not be filtered.
641
++ """
642
++
643
++
644
++ @pytest.mark.anyio
645
++ @pytest.mark.parametrize(
646
++ "spam_filter_after_n_txs, xch_spam_amount, dust_value",
647
++ [
648
++ # In the following tests, the filter is run right away:
649
++ (0, 1, 1), # nothing is filtered
650
++ # In the following tests, 1 coin will be created in part 1, and 9 in part 2:
651
++ (10, 10000000000, 1), # everything is dust
652
++ (10, 10000000000, 10000000000), # max dust threshold, dust is same size so not filtered
653
++ # Test with more coins
654
++ (105, 1000000, 1), # default filter level (1m mojos), default dust size (1)
655
++ ],
656
++ )
657
++ async def test_dusted_wallet(
658
++ self_hostname, two_wallet_nodes_custom_spam_filtering, spam_filter_after_n_txs, xch_spam_amount, dust_value
659
++ ):
660
++ full_nodes, wallets, _ = two_wallet_nodes_custom_spam_filtering
661
++
662
++ farm_wallet_node, farm_wallet_server = wallets[0]
663
++ dust_wallet_node, dust_wallet_server = wallets[1]
664
++
665
++ # Create two wallets, one for farming (not used for testing), and one for testing dust.
666
++ farm_wallet = farm_wallet_node.wallet_state_manager.main_wallet
667
++ dust_wallet = dust_wallet_node.wallet_state_manager.main_wallet
668
++ ph = await farm_wallet.get_new_puzzlehash()
669
++
670
++ full_node_api = full_nodes[0]
671
++
672
++ # It's also possible to obtain the current settings for spam_filter_after_n_txs and xch_spam_amount
673
++ # spam_filter_after_n_txs = wallets[0][0].config["spam_filter_after_n_txs"]
674
++ # xch_spam_amount = wallets[0][0].config["xch_spam_amount"]
675
++ # dust_value=1
676
++
677
++ # Verify legal values for the settings to be tested
678
++ # If spam_filter_after_n_txs is greater than 250, this test will take a long time to run.
679
++ # Current max value for xch_spam_amount is 0.01 XCH.
680
++ # If needed, this could be increased but we would need to farm more blocks.
681
++ # The max dust_value could be increased, but would require farming more blocks.
682
++ assert spam_filter_after_n_txs >= 0
683
++ assert spam_filter_after_n_txs <= 250
684
++ assert xch_spam_amount >= 1
685
++ assert xch_spam_amount <= 10000000000
686
++ assert dust_value >= 1
687
++ assert dust_value <= 10000000000
688
++
689
++ # start both clients
690
++ await farm_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
691
++ await dust_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
692
++
693
++ # Farm two blocks
694
++ for i in range(2):
695
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
722
696

723
-- # Part 1: create a single dust coin
724
-- payees: List[Payment] = []
697
++ # sync both nodes
698
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
699
++
700
++ # Part 1: create a single dust coin
701
++ payees: List[Payment] = []
702
++ payee_ph = await dust_wallet.get_new_puzzlehash()
703
++ payees.append(Payment(payee_ph, uint64(dust_value)))
704
++
705
++ # construct and send tx
706
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
707
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
708
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
709
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
710
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
711
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
712
++
713
++ # The dust is only filtered at this point if spam_filter_after_n_txs is 0 and xch_spam_amount is > dust_value.
714
++ if spam_filter_after_n_txs > 0:
715
++ dust_coins = 1
716
++ large_dust_coins = 0
717
++ large_dust_balance = 0
718
++ elif xch_spam_amount <= dust_value:
719
++ dust_coins = 0
720
++ large_dust_coins = 1
721
++ large_dust_balance = dust_value
722
++ else:
723
++ dust_coins = 0
724
++ large_dust_coins = 0
725
++ large_dust_balance = 0
726
++
727
++ # Obtain and log important values
728
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
729
++ log.info(f"all_unspent is {all_unspent}")
730
++ small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
731
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
732
++ num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance, DEFAULT_COIN_SELECTION_CONFIG))
733
++
734
++ log.info(f"Small coin count is {small_unspent_count}")
735
++ log.info(f"Wallet balance is {balance}")
736
++ log.info(f"Number of coins is {num_coins}")
737
++
738
++ log.info(f"spam_filter_after_n_txs {spam_filter_after_n_txs}")
739
++ log.info(f"xch_spam_amount {xch_spam_amount}")
740
++ log.info(f"dust_value {dust_value}")
741
++
742
++ # Verify balance and number of coins not filtered.
743
++ assert balance == dust_coins * dust_value + large_dust_balance
744
++ assert num_coins == dust_coins + large_dust_coins
745
++
746
++ # Part 2: Create dust coins until the filter threshold has been reached.
747
++ # Nothing should be filtered yet (unless spam_filter_after_n_txs is 0).
748
++ payees = []
749
++
750
++ # Determine how much dust to create, recalling that there already is one dust coin.
751
++ new_dust = spam_filter_after_n_txs - 1
752
++ dust_remaining = new_dust
753
++
754
++ while dust_remaining > 0:
725
755
payee_ph = await dust_wallet.get_new_puzzlehash()
726
756
payees.append(Payment(payee_ph, uint64(dust_value)))
727
757

728
-- # construct and send tx
729
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
730
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
731
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
732
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
733
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
734
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
735
--
736
-- # The dust is only filtered at this point if spam_filter_after_n_txs is 0 and xch_spam_amount is > dust_value.
737
-- if spam_filter_after_n_txs > 0:
738
-- dust_coins = 1
739
-- large_dust_coins = 0
740
-- large_dust_balance = 0
741
-- elif xch_spam_amount <= dust_value:
742
-- dust_coins = 0
743
-- large_dust_coins = 1
744
-- large_dust_balance = dust_value
745
-- else:
746
-- dust_coins = 0
747
-- large_dust_coins = 0
748
-- large_dust_balance = 0
749
--
750
-- # Obtain and log important values
751
-- all_unspent: Set[
752
-- WalletCoinRecord
753
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
754
-- log.info(f"all_unspent is {all_unspent}")
755
-- small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
756
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
757
-- num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance, DEFAULT_COIN_SELECTION_CONFIG))
758
--
759
-- log.info(f"Small coin count is {small_unspent_count}")
760
-- log.info(f"Wallet balance is {balance}")
761
-- log.info(f"Number of coins is {num_coins}")
762
--
763
-- log.info(f"spam_filter_after_n_txs {spam_filter_after_n_txs}")
764
-- log.info(f"xch_spam_amount {xch_spam_amount}")
765
-- log.info(f"dust_value {dust_value}")
766
--
767
-- # Verify balance and number of coins not filtered.
768
-- assert balance == dust_coins * dust_value + large_dust_balance
769
-- assert num_coins == dust_coins + large_dust_coins
770
--
771
-- # Part 2: Create dust coins until the filter threshold has been reached.
772
-- # Nothing should be filtered yet (unless spam_filter_after_n_txs is 0).
773
-- payees = []
774
--
775
-- # Determine how much dust to create, recalling that there already is one dust coin.
776
-- new_dust = spam_filter_after_n_txs - 1
777
-- dust_remaining = new_dust
778
--
779
-- while dust_remaining > 0:
780
-- payee_ph = await dust_wallet.get_new_puzzlehash()
781
-- payees.append(Payment(payee_ph, uint64(dust_value)))
782
--
783
-- # After every 100 (at most) coins added, push the tx and advance the chain
784
-- # This greatly speeds up the overall process
785
-- if dust_remaining % 100 == 0 and dust_remaining != new_dust:
786
-- # construct and send tx
787
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
788
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
789
--
790
-- # advance the chain and sync both wallets
791
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
792
-- await full_node_api.wait_for_wallets_synced(
793
-- wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
794
-- )
795
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
796
-- await full_node_api.wait_for_wallets_synced(
797
-- wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
798
-- )
799
-- # reset payees
800
-- payees = []
801
--
802
-- dust_remaining -= 1
803
--
804
-- # Only need to create tx if there was new dust to be added
805
-- if new_dust >= 1:
758
++ # After every 100 (at most) coins added, push the tx and advance the chain
759
++ # This greatly speeds up the overall process
760
++ if dust_remaining % 100 == 0 and dust_remaining != new_dust:
806
761
# construct and send tx
807
762
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
808
763
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
@@@ -812,44 -812,44 +767,13 @@@
812
767
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
813
768
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
814
769
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
770
++ # reset payees
771
++ payees = []
815
772

816
-- # Obtain and log important values
817
-- all_unspent: Set[
818
-- WalletCoinRecord
819
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
820
-- small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
821
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
822
-- # Selecting coins by using the wallet's coin selection algorithm won't work for large
823
-- # numbers of coins, so we'll use the state manager for the rest of the test
824
-- # num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance))
825
-- num_coins: Optional[Message] = len(
826
-- list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
827
-- )
828
--
829
-- log.info(f"Small coin count is {small_unspent_count}")
830
-- log.info(f"Wallet balance is {balance}")
831
-- log.info(f"Number of coins is {num_coins}")
832
--
833
-- # obtain the total expected coins (new_dust could be negative)
834
-- if new_dust > 0:
835
-- dust_coins += new_dust
836
--
837
-- # Make sure the number of coins matches the expected number.
838
-- # At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
839
-- assert dust_coins == spam_filter_after_n_txs
840
-- assert balance == dust_coins * dust_value + large_dust_balance
841
-- assert num_coins == dust_coins + large_dust_coins
842
--
843
-- # Part 3: Create 10 coins that are exactly the size of the filter threshold.
844
-- # These should not get filtered.
845
-- large_coins = 10
846
--
847
-- payees = []
848
--
849
-- for i in range(large_coins):
850
-- payee_ph = await dust_wallet.get_new_puzzlehash()
851
-- payees.append(Payment(payee_ph, uint64(xch_spam_amount)))
773
++ dust_remaining -= 1
852
774

775
++ # Only need to create tx if there was new dust to be added
776
++ if new_dust >= 1:
853
777
# construct and send tx
854
778
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
855
779
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
@@@ -860,485 -860,485 +784,505 @@@
860
784
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
861
785
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
862
786

863
-- # Obtain and log important values
864
-- all_unspent: Set[
865
-- WalletCoinRecord
866
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
867
-- small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
868
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
869
-- num_coins: Optional[Message] = len(
870
-- list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
871
-- )
872
--
873
-- log.info(f"Small coin count is {small_unspent_count}")
874
-- log.info(f"Wallet balance is {balance}")
875
-- log.info(f"Number of coins is {num_coins}")
876
--
877
-- large_coin_balance = large_coins * xch_spam_amount
878
--
879
-- # Determine whether the filter should have been activated.
880
-- # Make sure the number of coins matches the expected number.
881
-- # At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
882
-- assert dust_coins == spam_filter_after_n_txs
883
-- assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
884
-- assert num_coins == dust_coins + large_coins + large_dust_coins
885
--
886
-- # Part 4: Create one more dust coin to test the threshold
887
-- payees = []
888
--
787
++ # Obtain and log important values
788
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
789
++ small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
790
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
791
++ # Selecting coins by using the wallet's coin selection algorithm won't work for large
792
++ # numbers of coins, so we'll use the state manager for the rest of the test
793
++ # num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance))
794
++ spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
795
++ num_coins = len(spendable_coins)
796
++
797
++ log.info(f"Small coin count is {small_unspent_count}")
798
++ log.info(f"Wallet balance is {balance}")
799
++ log.info(f"Number of coins is {num_coins}")
800
++
801
++ # obtain the total expected coins (new_dust could be negative)
802
++ if new_dust > 0:
803
++ dust_coins += new_dust
804
++
805
++ # Make sure the number of coins matches the expected number.
806
++ # At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
807
++ assert dust_coins == spam_filter_after_n_txs
808
++ assert balance == dust_coins * dust_value + large_dust_balance
809
++ assert num_coins == dust_coins + large_dust_coins
810
++
811
++ # Part 3: Create 10 coins that are exactly the size of the filter threshold.
812
++ # These should not get filtered.
813
++ large_coins = 10
814
++
815
++ payees = []
816
++
817
++ for i in range(large_coins):
818
++ payee_ph = await dust_wallet.get_new_puzzlehash()
819
++ payees.append(Payment(payee_ph, uint64(xch_spam_amount)))
820
++
821
++ # construct and send tx
822
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
823
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
824
++
825
++ # advance the chain and sync both wallets
826
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
827
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
828
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
829
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
830
++
831
++ # Obtain and log important values
832
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
833
++ small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
834
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
835
++ spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
836
++ num_coins = len(spendable_coins)
837
++
838
++ log.info(f"Small coin count is {small_unspent_count}")
839
++ log.info(f"Wallet balance is {balance}")
840
++ log.info(f"Number of coins is {num_coins}")
841
++
842
++ large_coin_balance = large_coins * xch_spam_amount
843
++
844
++ # Determine whether the filter should have been activated.
845
++ # Make sure the number of coins matches the expected number.
846
++ # At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
847
++ assert dust_coins == spam_filter_after_n_txs
848
++ assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
849
++ assert num_coins == dust_coins + large_coins + large_dust_coins
850
++
851
++ # Part 4: Create one more dust coin to test the threshold
852
++ payees = []
853
++
854
++ payee_ph = await dust_wallet.get_new_puzzlehash()
855
++ payees.append(Payment(payee_ph, uint64(dust_value)))
856
++
857
++ # construct and send tx
858
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
859
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
860
++
861
++ # advance the chain and sync both wallets
862
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
863
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
864
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
865
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
866
++
867
++ # Obtain and log important values
868
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
869
++ small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
870
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
871
++ spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
872
++ num_coins = len(spendable_coins)
873
++
874
++ log.info(f"Small coin count is {small_unspent_count}")
875
++ log.info(f"Wallet balance is {balance}")
876
++ log.info(f"Number of coins is {num_coins}")
877
++
878
++ # In the edge case where the new "dust" is larger than the threshold,
879
++ # then it is actually a large dust coin that won't get filtered.
880
++ if dust_value >= xch_spam_amount:
881
++ large_dust_coins += 1
882
++ large_dust_balance += dust_value
883
++
884
++ assert dust_coins == spam_filter_after_n_txs
885
++ assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
886
++ assert num_coins == dust_coins + large_dust_coins + large_coins
887
++
888
++ # Part 5: Create 5 coins below the threshold and 5 at or above.
889
++ # Those below the threshold should get filtered, and those above should not.
890
++ payees = []
891
++
892
++ for i in range(5):
889
893
payee_ph = await dust_wallet.get_new_puzzlehash()
890
-- payees.append(Payment(payee_ph, uint64(dust_value)))
891
--
892
-- # construct and send tx
893
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
894
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
895
--
896
-- # advance the chain and sync both wallets
897
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
898
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
899
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
900
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
901
894

902
-- # Obtain and log important values
903
-- all_unspent: Set[
904
-- WalletCoinRecord
905
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
906
-- small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
907
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
908
-- num_coins: Optional[Message] = len(
909
-- list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
910
-- )
895
++ # Create a large coin and add on the appropriate balance.
896
++ payees.append(Payment(payee_ph, uint64(xch_spam_amount + i)))
897
++ large_coins += 1
898
++ large_coin_balance += xch_spam_amount + i
911
899

912
-- log.info(f"Small coin count is {small_unspent_count}")
913
-- log.info(f"Wallet balance is {balance}")
914
-- log.info(f"Number of coins is {num_coins}")
900
++ payee_ph = await dust_wallet.get_new_puzzlehash()
915
901

916
-- # In the edge case where the new "dust" is larger than the threshold,
917
-- # then it is actually a large dust coin that won't get filtered.
918
-- if dust_value >= xch_spam_amount:
902
++ # Make sure we are always creating coins with a positive value.
903
++ if xch_spam_amount - dust_value - i > 0:
904
++ payees.append(Payment(payee_ph, uint64(xch_spam_amount - dust_value - i)))
905
++ else:
906
++ payees.append(Payment(payee_ph, uint64(dust_value)))
907
++ # In cases where xch_spam_amount is sufficiently low,
908
++ # the new dust should be considered a large coina and not be filtered.
909
++ if xch_spam_amount <= dust_value:
919
910
large_dust_coins += 1
920
911
large_dust_balance += dust_value
921
912

922
-- assert dust_coins == spam_filter_after_n_txs
923
-- assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
924
-- assert num_coins == dust_coins + large_dust_coins + large_coins
925
--
926
-- # Part 5: Create 5 coins below the threshold and 5 at or above.
927
-- # Those below the threshold should get filtered, and those above should not.
928
-- payees = []
929
--
930
-- for i in range(5):
931
-- payee_ph = await dust_wallet.get_new_puzzlehash()
913
++ # construct and send tx
914
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
915
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
916
++
917
++ # advance the chain and sync both wallets
918
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
919
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
920
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
921
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
922
++
923
++ # Obtain and log important values
924
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
925
++ small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
926
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
927
++ spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
928
++ num_coins = len(spendable_coins)
929
++
930
++ log.info(f"Small coin count is {small_unspent_count}")
931
++ log.info(f"Wallet balance is {balance}")
932
++ log.info(f"Number of coins is {num_coins}")
933
++
934
++ # The filter should have automatically been activated by now, regardless of filter value
935
++ assert dust_coins == spam_filter_after_n_txs
936
++ assert balance == dust_coins * dust_value + large_coin_balance + large_dust_balance
937
++ assert num_coins == dust_coins + large_dust_coins + large_coins
938
++
939
++ # Part 6: Clear all coins from the dust wallet.
940
++ # Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
941
++ # Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
942
++
943
++ payee_ph = await farm_wallet.get_new_puzzlehash()
944
++ payees = [Payment(payee_ph, uint64(balance))]
945
++
946
++ # construct and send tx
947
++ [tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
948
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
949
++
950
++ # advance the chain and sync both wallets
951
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
952
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
953
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
954
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
955
++
956
++ # Obtain and log important values
957
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
958
++ unspent_count = len(all_unspent)
959
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
960
++
961
++ # Make sure the dust wallet is empty
962
++ assert unspent_count == 0
963
++ assert balance == 0
964
++
965
++ # create the same number of dust coins as the filter
966
++ if spam_filter_after_n_txs > 0:
967
++ coins_remaining = spam_filter_after_n_txs
968
++ else:
969
++ # in the edge case, create one coin
970
++ coins_remaining = 1
971
++
972
++ # The size of the coin to send the dust wallet is the same as xch_spam_amount
973
++ if xch_spam_amount > 1:
974
++ coin_value = xch_spam_amount
975
++ else:
976
++ # Handle the edge case to make sure the coin is at least 2 mojos
977
++ # This is needed to receive change
978
++ coin_value = 2
979
++
980
++ while coins_remaining > 0:
981
++ payee_ph = await dust_wallet.get_new_puzzlehash()
982
++ payees.append(Payment(payee_ph, uint64(coin_value)))
932
983

933
-- # Create a large coin and add on the appropriate balance.
934
-- payees.append(Payment(payee_ph, uint64(xch_spam_amount + i)))
935
-- large_coins += 1
936
-- large_coin_balance += xch_spam_amount + i
984
++ # After every 100 (at most) coins added, push the tx and advance the chain
985
++ # This greatly speeds up the overall process
986
++ if coins_remaining % 100 == 0 and coins_remaining != spam_filter_after_n_txs:
987
++ # construct and send tx
988
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
989
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
990
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
991
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
992
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
993
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
994
++ # reset payees
995
++ payees = []
996
++
997
++ coins_remaining -= 1
998
++
999
++ # construct and send tx
1000
++ [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
1001
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1002
++
1003
++ # advance the chain and sync both wallets
1004
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
1005
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1006
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1007
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1008
++
1009
++ # Obtain and log important values
1010
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1011
++ unspent_count = len(all_unspent)
1012
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
1013
++
1014
++ # Verify the number of coins and value
1015
++ if spam_filter_after_n_txs > 0:
1016
++ assert unspent_count == spam_filter_after_n_txs
1017
++ else:
1018
++ # in the edge case there should be 1 coin
1019
++ assert unspent_count == 1
1020
++ assert balance == unspent_count * coin_value
1021
++
1022
++ # Send a 1 mojo coin from the dust wallet to the farm wallet
1023
++ payee_ph = await farm_wallet.get_new_puzzlehash()
1024
++ payees = [Payment(payee_ph, uint64(1))]
1025
++
1026
++ # construct and send tx
1027
++ [tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
1028
++ await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1029
++
1030
++ # advance the chain and sync both wallets
1031
++ await full_node_api.wait_transaction_records_entered_mempool([tx])
1032
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1033
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1034
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1035
++
1036
++ # Obtain and log important values
1037
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1038
++ unspent_count = len(all_unspent)
1039
++ balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
1040
++
1041
++ # Make sure the dust wallet received a change coin worth 1 mojo less than the original coin size
1042
++ if spam_filter_after_n_txs > 0:
1043
++ assert unspent_count == spam_filter_after_n_txs
1044
++ else:
1045
++ # in the edge case there should be 1 coin
1046
++ assert unspent_count == 1
1047
++ assert balance == (unspent_count * coin_value) - 1
1048
++
1049
++ # Part 7: Create NFT wallets for the farmer and dust wallets.
1050
++ # Generate an NFT in the farmer wallet.
1051
++ # Send the NFT to the dust wallet, which already has enough coins to trigger the dust filter.
1052
++ # The NFT should not be filtered.
1053
++
1054
++ # Start with new puzzlehashes for each wallet
1055
++ farm_ph = await farm_wallet.get_new_puzzlehash()
1056
++ dust_ph = await dust_wallet.get_new_puzzlehash()
1057
++
1058
++ # Create an NFT wallet for the farmer and dust wallet
1059
++ farm_nft_wallet = await NFTWallet.create_new_nft_wallet(
1060
++ farm_wallet_node.wallet_state_manager, farm_wallet, name="FARM NFT WALLET"
1061
++ )
1062
++ dust_nft_wallet = await NFTWallet.create_new_nft_wallet(
1063
++ dust_wallet_node.wallet_state_manager, dust_wallet, name="DUST NFT WALLET"
1064
++ )
937
1065

938
-- payee_ph = await dust_wallet.get_new_puzzlehash()
1066
++ # Create a new NFT and send it to the farmer's NFT wallet
1067
++ metadata = Program.to(
1068
++ [
1069
++ ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
1070
++ ("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
1071
++ ]
1072
++ )
1073
++ txs = await farm_nft_wallet.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
1074
++ txs = await farm_nft_wallet.wallet_state_manager.add_pending_transactions(txs)
1075
++ for tx in txs:
1076
++ if tx.spend_bundle is not None:
1077
++ assert compute_memos(tx.spend_bundle)
1078
++ await time_out_assert_not_none(
1079
++ 20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
1080
++ )
939
1081

940
-- # Make sure we are always creating coins with a positive value.
941
-- if xch_spam_amount - dust_value - i > 0:
942
-- payees.append(Payment(payee_ph, uint64(xch_spam_amount - dust_value - i)))
1082
++ # Farm a new block
1083
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1084
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
1085
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1086
++
1087
++ # Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
1088
++ # if it were a normal dust coin (and not an NFT)
1089
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1090
++ unspent_count = len(all_unspent)
1091
++ assert unspent_count >= spam_filter_after_n_txs
1092
++
1093
++ # Make sure the NFT is in the farmer's NFT wallet, and the dust NFT wallet is empty
1094
++ await time_out_assert(15, get_nft_count, 1, farm_nft_wallet)
1095
++ await time_out_assert(15, get_nft_count, 0, dust_nft_wallet)
1096
++
1097
++ nft_coins = await farm_nft_wallet.get_current_nfts()
1098
++ # Send the NFT to the dust wallet
1099
++ txs = await farm_nft_wallet.generate_signed_transaction(
1100
++ [uint64(nft_coins[0].coin.amount)],
1101
++ [dust_ph],
1102
++ DEFAULT_TX_CONFIG,
1103
++ coins={nft_coins[0].coin},
1104
++ )
1105
++ assert len(txs) == 1
1106
++ assert txs[0].spend_bundle is not None
1107
++ await farm_wallet_node.wallet_state_manager.add_pending_transactions(txs)
1108
++ assert compute_memos(txs[0].spend_bundle)
1109
++
1110
++ # Farm a new block.
1111
++ await full_node_api.wait_transaction_records_entered_mempool(txs)
1112
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1113
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
1114
++ await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1115
++
1116
++ # Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
1117
++ # if it were a normal dust coin (and not an NFT)
1118
++ all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1119
++ unspent_count = len(all_unspent)
1120
++ assert unspent_count >= spam_filter_after_n_txs
1121
++
1122
++ # The dust wallet should now hold the NFT. It should not be filtered
1123
++ await time_out_assert(15, get_nft_count, 0, farm_nft_wallet)
1124
++ await time_out_assert(15, get_nft_count, 1, dust_nft_wallet)
1125
++
1126
++
1127
++ @pytest.mark.anyio
1128
++ async def test_retry_store(two_wallet_nodes, self_hostname):
1129
++ full_nodes, wallets, bt = two_wallet_nodes
1130
++ full_node_api = full_nodes[0]
1131
++ full_node_server = full_node_api.full_node.server
1132
++
1133
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
1134
++
1135
++ # Trusted node sync
1136
++ wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
1137
++
1138
++ # Untrusted node sync
1139
++ wallets[1][0].config["trusted_peers"] = {}
1140
++
1141
++ def flaky_get_coin_state(node, func):
1142
++ async def new_func(*args, **kwargs):
1143
++ if node.coin_state_flaky:
1144
++ node.coin_state_flaky = False
1145
++ raise PeerRequestException()
943
1146
else:
944
-- payees.append(Payment(payee_ph, uint64(dust_value)))
945
-- # In cases where xch_spam_amount is sufficiently low,
946
-- # the new dust should be considered a large coina and not be filtered.
947
-- if xch_spam_amount <= dust_value:
948
-- large_dust_coins += 1
949
-- large_dust_balance += dust_value
950
--
951
-- # construct and send tx
952
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
953
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1147
++ return await func(*args, **kwargs)
954
1148

955
-- # advance the chain and sync both wallets
956
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
957
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
958
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
959
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
960
--
961
-- # Obtain and log important values
962
-- all_unspent: Set[
963
-- WalletCoinRecord
964
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
965
-- small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
966
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
967
-- num_coins: Optional[Message] = len(
968
-- list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
969
-- )
970
--
971
-- log.info(f"Small coin count is {small_unspent_count}")
972
-- log.info(f"Wallet balance is {balance}")
973
-- log.info(f"Number of coins is {num_coins}")
974
--
975
-- # The filter should have automatically been activated by now, regardless of filter value
976
-- assert dust_coins == spam_filter_after_n_txs
977
-- assert balance == dust_coins * dust_value + large_coin_balance + large_dust_balance
978
-- assert num_coins == dust_coins + large_dust_coins + large_coins
979
--
980
-- # Part 6: Clear all coins from the dust wallet.
981
-- # Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
982
-- # Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
983
--
984
-- payee_ph = await farm_wallet.get_new_puzzlehash()
985
-- payees = [Payment(payee_ph, uint64(balance))]
986
--
987
-- # construct and send tx
988
-- [tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
989
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1149
++ return new_func
990
1150

991
-- # advance the chain and sync both wallets
992
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
993
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
994
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
995
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
996
--
997
-- # Obtain and log important values
998
-- all_unspent: Set[
999
-- WalletCoinRecord
1000
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1001
-- unspent_count = len([r for r in all_unspent])
1002
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
1003
--
1004
-- # Make sure the dust wallet is empty
1005
-- assert unspent_count == 0
1006
-- assert balance == 0
1007
--
1008
-- # create the same number of dust coins as the filter
1009
-- if spam_filter_after_n_txs > 0:
1010
-- coins_remaining = spam_filter_after_n_txs
1011
-- else:
1012
-- # in the edge case, create one coin
1013
-- coins_remaining = 1
1014
--
1015
-- # The size of the coin to send the dust wallet is the same as xch_spam_amount
1016
-- if xch_spam_amount > 1:
1017
-- coin_value = xch_spam_amount
1018
-- else:
1019
-- # Handle the edge case to make sure the coin is at least 2 mojos
1020
-- # This is needed to receive change
1021
-- coin_value = 2
1022
--
1023
-- while coins_remaining > 0:
1024
-- payee_ph = await dust_wallet.get_new_puzzlehash()
1025
-- payees.append(Payment(payee_ph, uint64(coin_value)))
1026
--
1027
-- # After every 100 (at most) coins added, push the tx and advance the chain
1028
-- # This greatly speeds up the overall process
1029
-- if coins_remaining % 100 == 0 and coins_remaining != spam_filter_after_n_txs:
1030
-- # construct and send tx
1031
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
1032
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1033
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
1034
-- await full_node_api.wait_for_wallets_synced(
1035
-- wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
1036
-- )
1037
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1038
-- await full_node_api.wait_for_wallets_synced(
1039
-- wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
1040
-- )
1041
-- # reset payees
1042
-- payees = []
1043
--
1044
-- coins_remaining -= 1
1045
--
1046
-- # construct and send tx
1047
-- [tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
1048
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1151
++ request_puzzle_solution_failure_tested = False
1049
1152

1050
-- # advance the chain and sync both wallets
1051
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
1052
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1053
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1054
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1153
++ def flaky_request_puzzle_solution(func):
1154
++ @functools.wraps(func)
1155
++ async def new_func(*args, **kwargs):
1156
++ nonlocal request_puzzle_solution_failure_tested
1157
++ if not request_puzzle_solution_failure_tested:
1158
++ request_puzzle_solution_failure_tested = True
1159
++ # This can just return None if we have `none_response` enabled.
1160
++ reject = wallet_protocol.RejectPuzzleSolution(bytes32([0] * 32), uint32(0))
1161
++ return make_msg(ProtocolMessageTypes.reject_puzzle_solution, reject)
1162
++ else:
1163
++ return await func(*args, **kwargs)
1055
1164

1056
-- # Obtain and log important values
1057
-- all_unspent: Set[
1058
-- WalletCoinRecord
1059
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1060
-- unspent_count = len([r for r in all_unspent])
1061
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
1165
++ return new_func
1062
1166

1063
-- # Verify the number of coins and value
1064
-- if spam_filter_after_n_txs > 0:
1065
-- assert unspent_count == spam_filter_after_n_txs
1066
-- else:
1067
-- # in the edge case there should be 1 coin
1068
-- assert unspent_count == 1
1069
-- assert balance == unspent_count * coin_value
1167
++ def flaky_fetch_children(node, func):
1168
++ async def new_func(*args, **kwargs):
1169
++ if node.fetch_children_flaky:
1170
++ node.fetch_children_flaky = False
1171
++ raise PeerRequestException()
1172
++ else:
1173
++ return await func(*args, **kwargs)
1070
1174

1071
-- # Send a 1 mojo coin from the dust wallet to the farm wallet
1072
-- payee_ph = await farm_wallet.get_new_puzzlehash()
1073
-- payees = [Payment(payee_ph, uint64(1))]
1175
++ return new_func
1074
1176

1075
-- # construct and send tx
1076
-- [tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
1077
-- await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
1177
++ def flaky_get_timestamp(node, func):
1178
++ async def new_func(*args, **kwargs):
1179
++ if node.get_timestamp_flaky:
1180
++ node.get_timestamp_flaky = False
1181
++ raise PeerRequestException()
1182
++ else:
1183
++ return await func(*args, **kwargs)
1078
1184

1079
-- # advance the chain and sync both wallets
1080
-- await full_node_api.wait_transaction_records_entered_mempool([tx])
1081
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1082
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1083
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1185
++ return new_func
1084
1186

1085
-- # Obtain and log important values
1086
-- all_unspent: Set[
1087
-- WalletCoinRecord
1088
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1089
-- unspent_count = len([r for r in all_unspent])
1090
-- balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
1187
++ def flaky_info_for_puzhash(node, func):
1188
++ async def new_func(*args, **kwargs):
1189
++ if node.db_flaky:
1190
++ node.db_flaky = False
1191
++ raise AIOSqliteError()
1192
++ else:
1193
++ return await func(*args, **kwargs)
1091
1194

1092
-- # Make sure the dust wallet received a change coin worth 1 mojo less than the original coin size
1093
-- if spam_filter_after_n_txs > 0:
1094
-- assert unspent_count == spam_filter_after_n_txs
1095
-- else:
1096
-- # in the edge case there should be 1 coin
1097
-- assert unspent_count == 1
1098
-- assert balance == (unspent_count * coin_value) - 1
1099
--
1100
-- # Part 7: Create NFT wallets for the farmer and dust wallets.
1101
-- # Generate an NFT in the farmer wallet.
1102
-- # Send the NFT to the dust wallet, which already has enough coins to trigger the dust filter.
1103
-- # The NFT should not be filtered.
1104
--
1105
-- # Start with new puzzlehashes for each wallet
1106
-- farm_ph = await farm_wallet.get_new_puzzlehash()
1107
-- dust_ph = await dust_wallet.get_new_puzzlehash()
1108
--
1109
-- # Create an NFT wallet for the farmer and dust wallet
1110
-- farm_nft_wallet = await NFTWallet.create_new_nft_wallet(
1111
-- farm_wallet_node.wallet_state_manager, farm_wallet, name="FARM NFT WALLET"
1112
-- )
1113
-- dust_nft_wallet = await NFTWallet.create_new_nft_wallet(
1114
-- dust_wallet_node.wallet_state_manager, dust_wallet, name="DUST NFT WALLET"
1115
-- )
1195
++ return new_func
1116
1196

1117
-- # Create a new NFT and send it to the farmer's NFT wallet
1118
-- metadata = Program.to(
1119
-- [
1120
-- ("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
1121
-- ("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
1122
-- ]
1123
-- )
1124
-- txs = await farm_nft_wallet.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
1125
-- await farm_nft_wallet.wallet_state_manager.add_pending_transactions(txs)
1126
-- for tx in txs:
1127
-- if tx.spend_bundle is not None:
1128
-- assert compute_memos(tx.spend_bundle)
1129
-- await time_out_assert_not_none(
1130
-- 20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
1131
-- )
1132
--
1133
-- # Farm a new block
1134
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1135
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
1136
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1197
++ full_node_api.request_puzzle_solution = flaky_request_puzzle_solution(full_node_api.request_puzzle_solution)
1137
1198

1138
-- # Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
1139
-- # if it were a normal dust coin (and not an NFT)
1140
-- all_unspent: Set[
1141
-- WalletCoinRecord
1142
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1143
-- unspent_count = len([r for r in all_unspent])
1144
-- assert unspent_count >= spam_filter_after_n_txs
1145
--
1146
-- # Make sure the NFT is in the farmer's NFT wallet, and the dust NFT wallet is empty
1147
-- await time_out_assert(15, get_nft_count, 1, farm_nft_wallet)
1148
-- await time_out_assert(15, get_nft_count, 0, dust_nft_wallet)
1149
--
1150
-- nft_coins = await farm_nft_wallet.get_current_nfts()
1151
-- # Send the NFT to the dust wallet
1152
-- txs = await farm_nft_wallet.generate_signed_transaction(
1153
-- [uint64(nft_coins[0].coin.amount)],
1154
-- [dust_ph],
1155
-- DEFAULT_TX_CONFIG,
1156
-- coins={nft_coins[0].coin},
1199
++ for wallet_node, wallet_server in wallets:
1200
++ wallet_node.coin_state_retry_seconds = 1
1201
++ request_puzzle_solution_failure_tested = False
1202
++ wallet_node.coin_state_flaky = True
1203
++ wallet_node.fetch_children_flaky = True
1204
++ wallet_node.get_timestamp_flaky = True
1205
++ wallet_node.db_flaky = True
1206
++
1207
++ wallet_node.get_coin_state = flaky_get_coin_state(wallet_node, wallet_node.get_coin_state)
1208
++ wallet_node.fetch_children = flaky_fetch_children(wallet_node, wallet_node.fetch_children)
1209
++ wallet_node.get_timestamp_for_height = flaky_get_timestamp(wallet_node, wallet_node.get_timestamp_for_height)
1210
++ wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash = flaky_info_for_puzhash(
1211
++ wallet_node, wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash
1157
1212
)
1158
-- assert len(txs) == 1
1159
-- assert txs[0].spend_bundle is not None
1160
-- await farm_wallet_node.wallet_state_manager.add_pending_transactions(txs)
1161
-- assert compute_memos(txs[0].spend_bundle)
1162
--
1163
-- # Farm a new block.
1164
-- await full_node_api.wait_transaction_records_entered_mempool(txs)
1165
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1166
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
1167
-- await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
1168
--
1169
-- # Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
1170
-- # if it were a normal dust coin (and not an NFT)
1171
-- all_unspent: Set[
1172
-- WalletCoinRecord
1173
-- ] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
1174
-- unspent_count = len([r for r in all_unspent])
1175
-- assert unspent_count >= spam_filter_after_n_txs
1176
--
1177
-- # The dust wallet should now hold the NFT. It should not be filtered
1178
-- await time_out_assert(15, get_nft_count, 0, farm_nft_wallet)
1179
-- await time_out_assert(15, get_nft_count, 1, dust_nft_wallet)
1180
1213

1181
-- @pytest.mark.anyio
1182
-- async def test_retry_store(self, two_wallet_nodes, self_hostname):
1183
-- full_nodes, wallets, bt = two_wallet_nodes
1184
-- full_node_api = full_nodes[0]
1185
-- full_node_server = full_node_api.full_node.server
1214
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1186
1215

1216
++ wallet = wallet_node.wallet_state_manager.main_wallet
1217
++ ph = await wallet.get_new_puzzlehash()
1218
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1187
1219
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
1188
1220

1189
-- # Trusted node sync
1190
-- wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
1191
--
1192
-- # Untrusted node sync
1193
-- wallets[1][0].config["trusted_peers"] = {}
1221
++ async def retry_store_empty() -> bool:
1222
++ return len(await wallet_node.wallet_state_manager.retry_store.get_all_states_to_retry()) == 0
1194
1223

1195
-- def flaky_get_coin_state(node, func):
1196
-- async def new_func(*args, **kwargs):
1197
-- if node.coin_state_flaky:
1198
-- node.coin_state_flaky = False
1199
-- raise PeerRequestException()
1200
-- else:
1201
-- return await func(*args, **kwargs)
1224
++ async def assert_coin_state_retry() -> None:
1225
++ # Wait for retry coin states to show up
1226
++ await time_out_assert(15, retry_store_empty, False)
1227
++ # And become retried/removed
1228
++ await time_out_assert(30, retry_store_empty, True)
1202
1229

1203
-- return new_func
1204
--
1205
-- request_puzzle_solution_failure_tested = False
1206
--
1207
-- def flaky_request_puzzle_solution(func):
1208
-- @functools.wraps(func)
1209
-- async def new_func(*args, **kwargs):
1210
-- nonlocal request_puzzle_solution_failure_tested
1211
-- if not request_puzzle_solution_failure_tested:
1212
-- request_puzzle_solution_failure_tested = True
1213
-- # This can just return None if we have `none_response` enabled.
1214
-- reject = wallet_protocol.RejectPuzzleSolution(bytes32([0] * 32), uint32(0))
1215
-- return make_msg(ProtocolMessageTypes.reject_puzzle_solution, reject)
1216
-- else:
1217
-- return await func(*args, **kwargs)
1218
--
1219
-- return new_func
1220
--
1221
-- def flaky_fetch_children(node, func):
1222
-- async def new_func(*args, **kwargs):
1223
-- if node.fetch_children_flaky:
1224
-- node.fetch_children_flaky = False
1225
-- raise PeerRequestException()
1226
-- else:
1227
-- return await func(*args, **kwargs)
1228
--
1229
-- return new_func
1230
--
1231
-- def flaky_get_timestamp(node, func):
1232
-- async def new_func(*args, **kwargs):
1233
-- if node.get_timestamp_flaky:
1234
-- node.get_timestamp_flaky = False
1235
-- raise PeerRequestException()
1236
-- else:
1237
-- return await func(*args, **kwargs)
1238
--
1239
-- return new_func
1240
--
1241
-- def flaky_info_for_puzhash(node, func):
1242
-- async def new_func(*args, **kwargs):
1243
-- if node.db_flaky:
1244
-- node.db_flaky = False
1245
-- raise AIOSqliteError()
1246
-- else:
1247
-- return await func(*args, **kwargs)
1248
--
1249
-- return new_func
1250
--
1251
-- full_node_api.request_puzzle_solution = flaky_request_puzzle_solution(full_node_api.request_puzzle_solution)
1252
--
1253
-- for wallet_node, wallet_server in wallets:
1254
-- wallet_node.coin_state_retry_seconds = 1
1255
-- request_puzzle_solution_failure_tested = False
1256
-- wallet_node.coin_state_flaky = True
1257
-- wallet_node.fetch_children_flaky = True
1258
-- wallet_node.get_timestamp_flaky = True
1259
-- wallet_node.db_flaky = True
1260
--
1261
-- wallet_node.get_coin_state = flaky_get_coin_state(wallet_node, wallet_node.get_coin_state)
1262
-- wallet_node.fetch_children = flaky_fetch_children(wallet_node, wallet_node.fetch_children)
1263
-- wallet_node.get_timestamp_for_height = flaky_get_timestamp(
1264
-- wallet_node, wallet_node.get_timestamp_for_height
1265
-- )
1266
-- wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash = (
1267
-- flaky_info_for_puzhash(
1268
-- wallet_node, wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash
1269
-- )
1270
-- )
1230
++ await assert_coin_state_retry()
1271
1231

1272
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1232
++ await time_out_assert(30, wallet.get_confirmed_balance, 2_000_000_000_000)
1273
1233

1274
-- wallet = wallet_node.wallet_state_manager.main_wallet
1275
-- ph = await wallet.get_new_puzzlehash()
1276
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1277
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
1278
--
1279
-- async def retry_store_empty() -> bool:
1280
-- return len(await wallet_node.wallet_state_manager.retry_store.get_all_states_to_retry()) == 0
1281
--
1282
-- async def assert_coin_state_retry() -> None:
1283
-- # Wait for retry coin states to show up
1284
-- await time_out_assert(15, retry_store_empty, False)
1285
-- # And become retried/removed
1286
-- await time_out_assert(30, retry_store_empty, True)
1287
--
1288
-- await assert_coin_state_retry()
1289
--
1290
-- await time_out_assert(30, wallet.get_confirmed_balance, 2_000_000_000_000)
1291
--
1292
-- [tx] = await wallet.generate_signed_transaction(
1293
-- 1_000_000_000_000, bytes32([0] * 32), DEFAULT_TX_CONFIG, memos=[ph]
1294
-- )
1295
-- await wallet_node.wallet_state_manager.add_pending_transactions([tx])
1296
--
1297
-- async def tx_in_mempool():
1298
-- return full_node_api.full_node.mempool_manager.get_spendbundle(tx.name) is not None
1299
--
1300
-- await time_out_assert(15, tx_in_mempool)
1301
-- await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
1302
--
1303
-- await assert_coin_state_retry()
1304
--
1305
-- assert not wallet_node.coin_state_flaky
1306
-- assert request_puzzle_solution_failure_tested
1307
-- assert not wallet_node.fetch_children_flaky
1308
-- assert not wallet_node.get_timestamp_flaky
1309
-- assert not wallet_node.db_flaky
1310
-- await time_out_assert(30, wallet.get_confirmed_balance, 1_000_000_000_000)
1311
--
1312
-- @pytest.mark.limit_consensus_modes(reason="save time")
1313
-- @pytest.mark.anyio
1314
-- async def test_bad_peak_mismatch(self, two_wallet_nodes, default_1000_blocks, self_hostname, blockchain_constants):
1315
-- full_nodes, wallets, bt = two_wallet_nodes
1316
-- wallet_node, wallet_server = wallets[0]
1317
-- full_node_api = full_nodes[0]
1318
-- full_node_server = full_node_api.full_node.server
1319
-- blocks = default_1000_blocks
1320
-- header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
1321
-- blocks, blockchain_constants
1234
++ [tx] = await wallet.generate_signed_transaction(
1235
++ 1_000_000_000_000, bytes32([0] * 32), DEFAULT_TX_CONFIG, memos=[ph]
1322
1236
)
1323
-- wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
1237
++ await wallet_node.wallet_state_manager.add_pending_transactions([tx])
1324
1238

1325
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1326
--
1327
-- for block in blocks:
1328
-- await full_node_api.full_node.add_block(block)
1239
++ async def tx_in_mempool():
1240
++ return full_node_api.full_node.mempool_manager.get_spendbundle(tx.name) is not None
1329
1241

1330
-- await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1242
++ await time_out_assert(15, tx_in_mempool)
1243
++ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
1331
1244

1332
-- # make wp for lower height
1333
-- wp = await wpf.get_proof_of_weight(height_to_hash[800])
1334
-- # create the node respond with the lighter proof
1335
-- wp_msg = make_msg(
1336
-- ProtocolMessageTypes.respond_proof_of_weight,
1337
-- full_node_protocol.RespondProofOfWeight(wp, wp.recent_chain_data[-1].header_hash),
1338
-- )
1245
++ await assert_coin_state_retry()
1246
++
1247
++ assert not wallet_node.coin_state_flaky
1248
++ assert request_puzzle_solution_failure_tested
1249
++ assert not wallet_node.fetch_children_flaky
1250
++ assert not wallet_node.get_timestamp_flaky
1251
++ assert not wallet_node.db_flaky
1252
++ await time_out_assert(30, wallet.get_confirmed_balance, 1_000_000_000_000)
1253
++
1254
++
1255
++ @pytest.mark.limit_consensus_modes(reason="save time")
1256
++ @pytest.mark.anyio
1257
++ async def test_bad_peak_mismatch(
1258
++ two_wallet_nodes, default_1000_blocks, self_hostname, blockchain_constants, monkeypatch
1259
++ ):
1260
++ full_nodes, wallets, bt = two_wallet_nodes
1261
++ wallet_node, wallet_server = wallets[0]
1262
++ full_node_api = full_nodes[0]
1263
++ full_node_server = full_node_api.full_node.server
1264
++ blocks = default_1000_blocks
1265
++ header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks, blockchain_constants)
1266
++ wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
1267
++
1268
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1269
++
1270
++ for block in blocks:
1271
++ await full_node_api.full_node.add_block(block)
1272
++
1273
++ await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
1274
++
1275
++ # make wp for lower height
1276
++ wp = await wpf.get_proof_of_weight(height_to_hash[800])
1277
++ # create the node respond with the lighter proof
1278
++ wp_msg = make_msg(
1279
++ ProtocolMessageTypes.respond_proof_of_weight,
1280
++ full_node_protocol.RespondProofOfWeight(wp, wp.recent_chain_data[-1].header_hash),
1281
++ )
1282
++ with monkeypatch.context() as m:
1339
1283
f = asyncio.Future()
1340
1284
f.set_result(wp_msg)
1341
-- full_node_api.request_proof_of_weight = MagicMock(return_value=f)
1285
++ m.setattr(full_node_api, "request_proof_of_weight", MagicMock(return_value=f))
1342
1286

1343
1287
# create the node respond with the lighter header block
1344
1288
header_block_msg = make_msg(
@@@ -1347,7 -1347,7 +1291,7 @@@
1347
1291
)
1348
1292
f2 = asyncio.Future()
1349
1293
f2.set_result(header_block_msg)
1350
-- full_node_api.request_block_header = MagicMock(return_value=f2)
1294
++ m.setattr(full_node_api, "request_block_header", MagicMock(return_value=f2))
1351
1295

1352
1296
# create new fake peak msg
1353
1297
fake_peak_height = uint32(11000)
tests/wallet/test_bech32m.py CHANGED
@@@ -6,7 -6,7 +6,7 @@@ from __future__ import annotation
6
6
from chia.util.bech32m import bech32_decode
7
7

8
8

9
-- def test_valid_imports():
9
++ def test_valid_imports() -> None:
10
10
test_strings = [
11
11
"A1LQFN3A",
12
12
"a1lqfn3a",
@@@ -18,11 -18,11 +18,11 @@@
18
18
"?1v759aa",
19
19
]
20
20
for test_str in test_strings:
21
-- hrp, data = bech32_decode(test_str)
21
++ _, data = bech32_decode(test_str)
22
22
assert data is not None
23
23

24
24

25
-- def test_invalid_imports():
25
++ def test_invalid_imports() -> None:
26
26
test_strings = [
27
27
f"{0x20}1xj0phk",
28
28
f"{0x7F}1g6xzxy",
@@@ -41,5 -41,5 +41,5 @@@
41
41
"1p2gdwpf",
42
42
]
43
43
for test_str in test_strings:
44
-- hrp, data = bech32_decode(test_str)
44
++ _, data = bech32_decode(test_str)
45
45
assert data is None
tests/wallet/test_puzzle_store.py CHANGED
@@@ -42,61 -42,61 +42,60 @@@ class DummyDerivationRecords
42
42
self.index_per_wallet[wallet_id] += 1
43
43

44
44

45
-- class TestPuzzleStore:
46
-- @pytest.mark.anyio
47
-- async def test_puzzle_store(self, seeded_random: random.Random):
48
-- async with DBConnection(1) as wrapper:
49
-- db = await WalletPuzzleStore.create(wrapper)
50
-- derivation_recs = []
51
-- for i in range(1000):
52
-- derivation_recs.append(
53
-- DerivationRecord(
54
-- uint32(i),
55
-- bytes32.random(seeded_random),
56
-- AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
57
-- WalletType.STANDARD_WALLET,
58
-- uint32(1),
59
-- False,
60
-- )
45
++ @pytest.mark.anyio
46
++ async def test_puzzle_store(seeded_random: random.Random) -> None:
47
++ async with DBConnection(1) as wrapper:
48
++ db = await WalletPuzzleStore.create(wrapper)
49
++ derivation_recs = []
50
++ for i in range(1000):
51
++ derivation_recs.append(
52
++ DerivationRecord(
53
++ uint32(i),
54
++ bytes32.random(seeded_random),
55
++ AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
56
++ WalletType.STANDARD_WALLET,
57
++ uint32(1),
58
++ False,
61
59
)
62
-- derivation_recs.append(
63
-- DerivationRecord(
64
-- uint32(i),
65
-- bytes32.random(seeded_random),
66
-- AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
67
-- WalletType.CAT,
68
-- uint32(2),
69
-- False,
70
-- )
60
++ )
61
++ derivation_recs.append(
62
++ DerivationRecord(
63
++ uint32(i),
64
++ bytes32.random(seeded_random),
65
++ AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
66
++ WalletType.CAT,
67
++ uint32(2),
68
++ False,
71
69
)
72
-- assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is False
73
-- assert await db.index_for_pubkey(derivation_recs[0].pubkey) is None
74
-- assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
75
-- assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
76
-- assert len(await db.get_all_puzzle_hashes()) == 0
77
-- assert await db.get_last_derivation_path() is None
78
-- assert await db.get_unused_derivation_path() is None
79
-- assert await db.get_derivation_record(0, 2, False) is None
80
--
81
-- await db.add_derivation_paths(derivation_recs)
82
--
83
-- assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is True
84
--
85
-- assert await db.index_for_pubkey(derivation_recs[4].pubkey) == 2
86
-- assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) == 1
87
-- assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) == WalletIdentifier(
88
-- derivation_recs[2].wallet_id,
89
-- derivation_recs[2].wallet_type,
90
70
)
91
-- assert len(await db.get_all_puzzle_hashes()) == 2000
92
-- assert await db.get_last_derivation_path() == 999
93
-- assert await db.get_unused_derivation_path() == 0
94
-- assert await db.get_derivation_record(0, 2, False) == derivation_recs[1]
95
--
96
-- # Indeces up to 250
97
-- await db.set_used_up_to(249)
98
--
99
-- assert await db.get_unused_derivation_path() == 250
71
++ assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is False
72
++ assert await db.index_for_pubkey(derivation_recs[0].pubkey) is None
73
++ assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
74
++ assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
75
++ assert len(await db.get_all_puzzle_hashes()) == 0
76
++ assert await db.get_last_derivation_path() is None
77
++ assert await db.get_unused_derivation_path() is None
78
++ assert await db.get_derivation_record(0, 2, False) is None
79
++
80
++ await db.add_derivation_paths(derivation_recs)
81
++
82
++ assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is True
83
++
84
++ assert await db.index_for_pubkey(derivation_recs[4].pubkey) == 2
85
++ assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) == 1
86
++ assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) == WalletIdentifier(
87
++ derivation_recs[2].wallet_id,
88
++ derivation_recs[2].wallet_type,
89
++ )
90
++ assert len(await db.get_all_puzzle_hashes()) == 2000
91
++ assert await db.get_last_derivation_path() == 999
92
++ assert await db.get_unused_derivation_path() == 0
93
++ assert await db.get_derivation_record(0, 2, False) == derivation_recs[1]
94
++
95
++ # Indeces up to 250
96
++ await db.set_used_up_to(249)
97
++
98
++ assert await db.get_unused_derivation_path() == 250
100
99

101
100

102
101
@pytest.mark.anyio
tests/wallet/test_singleton.py CHANGED
@@@ -38,14 -38,14 +38,14 @@@ def p2_singleton_puzzle_hash(launcher_i
38
38
return p2_singleton_puzzle(launcher_id, launcher_puzzle_hash).get_tree_hash()
39
39

40
40

41
-- def test_only_odd_coins():
41
++ def test_only_odd_coins() -> None:
42
42
singleton_mod_hash = SINGLETON_MOD.get_tree_hash()
43
43
# (SINGLETON_STRUCT INNER_PUZZLE lineage_proof my_amount inner_solution)
44
44
# SINGLETON_STRUCT = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
45
45
solution = Program.to(
46
46
[
47
47
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
48
-- Program.to(binutils.assemble("(q (51 0xcafef00d 200))")),
48
++ Program.to(binutils.assemble("(q (51 0xcafef00d 200))")), # type: ignore[no-untyped-call]
49
49
[0xDEADBEEF, 0xCAFEF00D, 200],
50
50
200,
51
51
[],
@@@ -53,27 -53,27 +53,28 @@@
53
53
)
54
54

55
55
with pytest.raises(Exception) as exception_info:
56
-- cost, result = SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
56
++ SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
57
57
assert exception_info.value.args == ("clvm raise", "80")
58
58

59
59
solution = Program.to(
60
60
[
61
61
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
62
-- Program.to(binutils.assemble("(q (51 0xcafef00d 201))")),
62
++ Program.to(binutils.assemble("(q (51 0xcafef00d 201))")), # type: ignore[no-untyped-call]
63
63
[0xDEADBEEF, 0xCAFED00D, 210],
64
64
205,
65
65
0,
66
66
]
67
67
)
68
-- cost, result = SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
68
++ SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
69
69

70
70

71
-- def test_only_one_odd_coin_created():
71
++ def test_only_one_odd_coin_created() -> None:
72
72
singleton_mod_hash = SINGLETON_MOD.get_tree_hash()
73
++ clsp = "(q (51 0xcafef00d 203) (51 0xfadeddab 205))"
73
74
solution = Program.to(
74
75
[
75
76
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
76
-- Program.to(binutils.assemble("(q (51 0xcafef00d 203) (51 0xfadeddab 205))")),
77
++ Program.to(binutils.assemble(clsp)), # type: ignore[no-untyped-call]
77
78
[0xDEADBEEF, 0xCAFEF00D, 411],
78
79
411,
79
80
[],
@@@ -81,22 -81,22 +82,22 @@@
81
82
)
82
83

83
84
with pytest.raises(Exception) as exception_info:
84
-- cost, result = SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
85
++ SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
85
86
assert exception_info.value.args == ("clvm raise", "80")
86
--
87
++ clsp = "(q (51 0xcafef00d 203) (51 0xfadeddab 204) (51 0xdeadbeef 202))"
87
88
solution = Program.to(
88
89
[
89
90
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
90
-- Program.to(binutils.assemble("(q (51 0xcafef00d 203) (51 0xfadeddab 204) (51 0xdeadbeef 202))")),
91
++ Program.to(binutils.assemble(clsp)), # type: ignore[no-untyped-call]
91
92
[0xDEADBEEF, 0xCAFEF00D, 411],
92
93
411,
93
94
[],
94
95
]
95
96
)
96
-- cost, result = SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
97
++ SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
97
98

98
99

99
-- def test_p2_singleton():
100
++ def test_p2_singleton() -> None:
100
101
# create a singleton. This should call driver code.
101
102
launcher_id = LAUNCHER_ID
102
103
innerpuz = Program.to(1)
@@@ -111,11 -111,11 +112,11 @@@
111
112
# create a `p2_singleton` puzzle. This should call driver code.
112
113
p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
113
114
solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
114
-- cost, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
115
++ _, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
115
116
conditions = parse_sexp_to_conditions(result)
116
117

117
118
p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
118
119
solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
119
-- cost, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
120
++ _, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
120
121
assert result.first().rest().first().as_atom() == expected_announcement
121
122
assert conditions[0].vars[0] == expected_announcement
tests/wallet/test_singleton_lifecycle.py CHANGED
@@@ -47,7 -47,7 +47,7 @@@ def launcher_conditions_and_spend_bundl
47
47
initial_singleton_inner_puzzle: Program,
48
48
metadata: List[Tuple[str, str]],
49
49
launcher_puzzle: Program = LAUNCHER_PUZZLE,
50
-- ) -> Tuple[Program, bytes32, List[Program], SpendBundle]:
50
++ ) -> Tuple[bytes32, List[Program], SpendBundle]:
51
51
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
52
52
launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
53
53
singleton_full_puzzle = SINGLETON_MOD.curry(
@@@ -74,8 -74,8 +74,7 @@@
74
74
launcher_solution = Program.to([singleton_full_puzzle_hash, launcher_amount, metadata])
75
75
coin_spend = make_spend(launcher_coin, launcher_puzzle, launcher_solution)
76
76
spend_bundle = SpendBundle([coin_spend], G2Element())
77
-- lineage_proof = Program.to([parent_coin_id, launcher_amount])
78
-- return lineage_proof, launcher_coin.name(), expected_conditions, spend_bundle
77
++ return launcher_coin.name(), expected_conditions, spend_bundle
79
78

80
79

81
80
def singleton_puzzle(launcher_id: Program, launcher_puzzle_hash: bytes32, inner_puzzle: Program) -> Program:
@@@ -109,7 -109,7 +108,7 @@@ async def test_only_odd_coins_0(bt)
109
108
launcher_puzzle = LAUNCHER_PUZZLE
110
109
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
111
110
initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle(ANYONE_CAN_SPEND_PUZZLE)
112
-- lineage_proof, launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle(
111
++ launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle(
113
112
farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle
114
113
)
115
114

tests/wallet/test_singleton_lifecycle_fast.py CHANGED
@@@ -272,12 -272,12 +272,13 @@@ def launcher_conditions_and_spend_bundl
272
272
puzzle_db.add_puzzle(launcher_puzzle)
273
273
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
274
274
launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
275
-- singleton_full_puzzle = singleton_puzzle(launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_puzzle)
275
++ launcher_id = launcher_coin.name()
276
++ singleton_full_puzzle = singleton_puzzle(launcher_id, launcher_puzzle_hash, initial_singleton_inner_puzzle)
276
277
puzzle_db.add_puzzle(singleton_full_puzzle)
277
278
singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash()
278
279
message_program = Program.to([singleton_full_puzzle_hash, launcher_amount, metadata])
279
280
expected_announcement = AssertCoinAnnouncement(
280
-- asserted_id=launcher_coin.name(), asserted_msg=message_program.get_tree_hash()
281
++ asserted_id=launcher_id, asserted_msg=message_program.get_tree_hash()
281
282
)
282
283
expected_conditions = []
283
284
expected_conditions.append(
@@@ -301,7 -301,7 +302,7 @@@
301
302
)
302
303
coin_spend = make_spend(launcher_coin, SerializedProgram.from_program(launcher_puzzle), solution)
303
304
spend_bundle = SpendBundle([coin_spend], G2Element())
304
-- return launcher_coin.name(), expected_conditions, spend_bundle
305
++ return launcher_id, expected_conditions, spend_bundle
305
306

306
307

307
308
def singleton_puzzle(launcher_id: bytes32, launcher_puzzle_hash: bytes32, inner_puzzle: Program) -> Program:
@@@ -440,7 -440,7 +441,7 @@@ def spend_coin_to_singleton
440
441
return additions, removals
441
442

442
443

443
-- def find_interesting_singletons(puzzle_db: PuzzleDB, removals: List[CoinSpend]) -> List[SingletonWallet]:
444
++ def find_interesting_singletons(removals: List[CoinSpend]) -> List[SingletonWallet]:
444
445
singletons = []
445
446
for coin_spend in removals:
446
447
if coin_spend.coin.puzzle_hash == LAUNCHER_PUZZLE_HASH:
@@@ -462,7 -462,7 +463,7 @@@
462
463
return singletons
463
464

464
465

465
-- def filter_p2_singleton(puzzle_db: PuzzleDB, singleton_wallet: SingletonWallet, additions: List[Coin]) -> List[Coin]:
466
++ def filter_p2_singleton(puzzle_db: PuzzleDB, additions: List[Coin]) -> List[Coin]:
466
467
r = []
467
468
for coin in additions:
468
469
puzzle = puzzle_db.puzzle_for_hash(coin.puzzle_hash)
@@@ -495,7 -495,7 +496,7 @@@ def test_lifecycle_with_coinstore_as_wa
495
496

496
497
assert len(list(coin_store.all_unspent_coins())) == 1
497
498

498
-- new_singletons = find_interesting_singletons(PUZZLE_DB, removals)
499
++ new_singletons = find_interesting_singletons(removals)
499
500
interested_singletons.extend(new_singletons)
500
501

501
502
assert len(interested_singletons) == 1
@@@ -512,7 -512,7 +513,7 @@@
512
513
now.seconds += 500
513
514
now.height += 1
514
515

515
-- p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin])
516
++ p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
516
517
assert p2_singleton_coins == [farmed_coin]
517
518

518
519
assert len(list(coin_store.all_unspent_coins())) == 2
@@@ -544,7 -544,7 +545,7 @@@
544
545
now.seconds += 500
545
546
now.height += 1
546
547

547
-- p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin])
548
++ p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
548
549
assert p2_singleton_coins == [farmed_coin]
549
550

550
551
assert len(list(coin_store.all_unspent_coins())) == 2
@@@ -622,7 -622,7 +623,7 @@@
622
623
now.seconds += 500
623
624
now.height += 1
624
625

625
-- p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin])
626
++ p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
626
627
assert p2_singleton_coins == [farmed_coin]
627
628

628
629
assert len(list(coin_store.all_unspent_coins())) == 2
@@@ -676,7 -676,7 +677,7 @@@
676
677
now.seconds += 500
677
678
now.height += 1
678
679

679
-- p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, SINGLETON_WALLET, [farmed_coin])
680
++ p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
680
681
assert p2_singleton_coins == [farmed_coin]
681
682

682
683
assert len(list(coin_store.all_unspent_coins())) == 3
tests/wallet/test_util.py CHANGED
@@@ -1,5 -1,5 +1,7 @@@
1
1
from __future__ import annotations
2
2

3
++ from typing import Any, Dict, List, Tuple
4
++
3
5
import pytest
4
6

5
7
from chia.consensus.default_constants import DEFAULT_CONSTANTS
@@@ -9,7 -9,7 +11,9 @@@ from chia.types.blockchain_format.sized
9
11
from chia.types.coin_spend import make_spend
10
12
from chia.util.errors import ValidationError
11
13
from chia.util.ints import uint64
14
++ from chia.wallet.lineage_proof import LineageProof, LineageProofField
12
15
from chia.wallet.util.compute_hints import HintedCoin, compute_spend_hints_and_additions
16
++ from chia.wallet.util.merkle_utils import list_to_binary_tree
13
17
from chia.wallet.util.tx_config import (
14
18
DEFAULT_COIN_SELECTION_CONFIG,
15
19
DEFAULT_TX_CONFIG,
@@@ -92,3 -92,3 +96,86 @@@ def test_tx_config() -> None
92
96
assert TXConfigLoader.from_json_dict({}).autofill(
93
97
constants=DEFAULT_CONSTANTS, config={"reuse_public_key_for_change": {"1": True}}, logged_in_fingerprint=1
94
98
).to_json_dict() == {**default_tx_config, "reuse_puzhash": True}
99
++
100
++
101
++ def test_list_to_binary_tree() -> None:
102
++ assert list_to_binary_tree([1]) == 1
103
++ assert list_to_binary_tree([1, 2]) == (1, 2)
104
++ assert list_to_binary_tree([1, 2, 3]) == ((1, 2), 3)
105
++ assert list_to_binary_tree([1, 2, 3, 4]) == ((1, 2), (3, 4))
106
++ assert list_to_binary_tree([1, 2, 3, 4, 5]) == (((1, 2), 3), (4, 5))
107
++ with pytest.raises(ValueError):
108
++ list_to_binary_tree([])
109
++
110
++
111
++ @pytest.mark.parametrize(
112
++ "serializations",
113
++ [
114
++ (tuple(), Program.to(None), []),
115
++ ((bytes32([0] * 32),), Program.to([bytes32([0] * 32)]), [LineageProofField.PARENT_NAME]),
116
++ (
117
++ (bytes32([0] * 32), bytes32([0] * 32)),
118
++ Program.to([bytes32([0] * 32), bytes32([0] * 32)]),
119
++ [LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH],
120
++ ),
121
++ (
122
++ (bytes32([0] * 32), bytes32([0] * 32), uint64(0)),
123
++ Program.to([bytes32([0] * 32), bytes32([0] * 32), uint64(0)]),
124
++ [LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
125
++ ),
126
++ ],
127
++ )
128
++ def test_lineage_proof_varargs(serializations: Tuple[Tuple[Any, ...], Program, List[LineageProofField]]) -> None:
129
++ var_args, expected_program, lp_fields = serializations
130
++ assert LineageProof(*var_args).to_program() == expected_program
131
++ assert LineageProof(*var_args) == LineageProof.from_program(expected_program, lp_fields)
132
++
133
++
134
++ @pytest.mark.parametrize(
135
++ "serializations",
136
++ [
137
++ ({}, Program.to(None), []),
138
++ ({"parent_name": bytes32([0] * 32)}, Program.to([bytes32([0] * 32)]), [LineageProofField.PARENT_NAME]),
139
++ (
140
++ {"parent_name": bytes32([0] * 32), "inner_puzzle_hash": bytes32([0] * 32)},
141
++ Program.to([bytes32([0] * 32), bytes32([0] * 32)]),
142
++ [LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH],
143
++ ),
144
++ (
145
++ {"parent_name": bytes32([0] * 32), "inner_puzzle_hash": bytes32([0] * 32), "amount": uint64(0)},
146
++ Program.to([bytes32([0] * 32), bytes32([0] * 32), uint64(0)]),
147
++ [LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
148
++ ),
149
++ (
150
++ {"parent_name": bytes32([0] * 32), "amount": uint64(0)},
151
++ Program.to([bytes32([0] * 32), uint64(0)]),
152
++ [LineageProofField.PARENT_NAME, LineageProofField.AMOUNT],
153
++ ),
154
++ (
155
++ {"inner_puzzle_hash": bytes32([0] * 32), "amount": uint64(0)},
156
++ Program.to([bytes32([0] * 32), uint64(0)]),
157
++ [LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
158
++ ),
159
++ ({"amount": uint64(0)}, Program.to([uint64(0)]), [LineageProofField.AMOUNT]),
160
++ (
161
++ {"inner_puzzle_hash": bytes32([0] * 32)},
162
++ Program.to([bytes32([0] * 32)]),
163
++ [LineageProofField.INNER_PUZZLE_HASH],
164
++ ),
165
++ ],
166
++ )
167
++ def test_lineage_proof_kwargs(serializations: Tuple[Dict[str, Any], Program, List[LineageProofField]]) -> None:
168
++ kwargs, expected_program, lp_fields = serializations
169
++ assert LineageProof(**kwargs).to_program() == expected_program
170
++ assert LineageProof(**kwargs) == LineageProof.from_program(expected_program, lp_fields)
171
++
172
++
173
++ def test_lineage_proof_errors() -> None:
174
++ with pytest.raises(ValueError, match="Mismatch"):
175
++ LineageProof.from_program(Program.to([]), [LineageProofField.PARENT_NAME])
176
++ with pytest.raises(StopIteration):
177
++ LineageProof.from_program(Program.to([bytes32([0] * 32)]), [])
178
++ with pytest.raises(ValueError):
179
++ LineageProof.from_program(Program.to([bytes32([1] * 32)]), [LineageProofField.AMOUNT])
180
++ with pytest.raises(ValueError):
181
++ LineageProof.from_program(Program.to([uint64(0)]), [LineageProofField.PARENT_NAME])
tests/wallet/test_wallet_blockchain.py CHANGED
@@@ -1,97 -1,97 +1,116 @@@
1
1
from __future__ import annotations
2
2

3
3
import dataclasses
4
++ from typing import List
4
5

5
6
import pytest
6
7

7
8
from chia.consensus.blockchain import AddBlockResult
8
9
from chia.protocols import full_node_protocol
9
10
from chia.types.blockchain_format.vdf import VDFProof
10
-- from chia.types.weight_proof import WeightProof
11
++ from chia.types.full_block import FullBlock
12
++ from chia.types.header_block import HeaderBlock
11
13
from chia.util.generator_tools import get_block_header
14
++ from chia.util.ints import uint8, uint32
12
15
from chia.wallet.key_val_store import KeyValStore
13
16
from chia.wallet.wallet_blockchain import WalletBlockchain
14
17
from tests.conftest import ConsensusMode
15
18
from tests.util.db_connection import DBConnection
19
++ from tests.util.setup_nodes import OldSimulatorsAndWallets
16
20

17
21

18
-- class TestWalletBlockchain:
19
-- @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
20
-- @pytest.mark.anyio
21
-- async def test_wallet_blockchain(self, simulator_and_wallet, default_1000_blocks):
22
-- [full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
22
++ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
23
++ @pytest.mark.anyio
24
++ async def test_wallet_blockchain(
25
++ simulator_and_wallet: OldSimulatorsAndWallets, default_1000_blocks: List[FullBlock]
26
++ ) -> None:
27
++ [full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
23
28

24
-- for block in default_1000_blocks[:600]:
25
-- await full_node_api.full_node.add_block(block)
29
++ for block in default_1000_blocks[:600]:
30
++ await full_node_api.full_node.add_block(block)
26
31

27
-- res = await full_node_api.request_proof_of_weight(
28
-- full_node_protocol.RequestProofOfWeight(
29
-- default_1000_blocks[499].height + 1, default_1000_blocks[499].header_hash
30
-- )
32
++ resp = await full_node_api.request_proof_of_weight(
33
++ full_node_protocol.RequestProofOfWeight(
34
++ uint32(default_1000_blocks[499].height + 1), default_1000_blocks[499].header_hash
31
35
)
32
-- res_2 = await full_node_api.request_proof_of_weight(
33
-- full_node_protocol.RequestProofOfWeight(
34
-- default_1000_blocks[460].height + 1, default_1000_blocks[460].header_hash
35
-- )
36
++ )
37
++ assert resp is not None
38
++ resp_2 = await full_node_api.request_proof_of_weight(
39
++ full_node_protocol.RequestProofOfWeight(
40
++ uint32(default_1000_blocks[460].height + 1), default_1000_blocks[460].header_hash
36
41
)
37
--
38
-- res_3 = await full_node_api.request_proof_of_weight(
39
-- full_node_protocol.RequestProofOfWeight(
40
-- default_1000_blocks[505].height + 1, default_1000_blocks[505].header_hash
41
-- )
42
++ )
43
++ assert resp_2 is not None
44
++ resp_3 = await full_node_api.request_proof_of_weight(
45
++ full_node_protocol.RequestProofOfWeight(
46
++ uint32(default_1000_blocks[505].height + 1), default_1000_blocks[505].header_hash
42
47
)
43
-- weight_proof: WeightProof = full_node_protocol.RespondProofOfWeight.from_bytes(res.data).wp
44
-- records = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof, True)
45
-- weight_proof_short: WeightProof = full_node_protocol.RespondProofOfWeight.from_bytes(res_2.data).wp
46
-- records_short = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_short, True)
47
-- weight_proof_long: WeightProof = full_node_protocol.RespondProofOfWeight.from_bytes(res_3.data).wp
48
-- records_long = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_long, True)
49
--
50
-- async with DBConnection(1) as db_wrapper:
51
-- store = await KeyValStore.create(db_wrapper)
52
-- chain = await WalletBlockchain.create(store, bt.constants)
53
--
54
-- assert (await chain.get_peak_block()) is None
55
-- assert chain.get_latest_timestamp() == 0
56
--
57
-- await chain.new_valid_weight_proof(weight_proof, records)
58
-- assert (await chain.get_peak_block()) is not None
59
-- assert (await chain.get_peak_block()).height == 499
60
-- assert chain.get_latest_timestamp() > 0
61
--
62
-- await chain.new_valid_weight_proof(weight_proof_short, records_short)
63
-- assert (await chain.get_peak_block()).height == 499
64
--
65
-- await chain.new_valid_weight_proof(weight_proof_long, records_long)
66
-- assert (await chain.get_peak_block()).height == 505
67
--
68
-- header_blocks = []
69
-- for block in default_1000_blocks:
70
-- header_block = get_block_header(block, [], [])
71
-- header_blocks.append(header_block)
72
--
73
-- res, err = await chain.add_block(header_blocks[50])
74
-- print(res, err)
75
-- assert res == AddBlockResult.DISCONNECTED_BLOCK
76
--
77
-- res, err = await chain.add_block(header_blocks[400])
78
-- print(res, err)
79
-- assert res == AddBlockResult.ALREADY_HAVE_BLOCK
80
--
81
-- res, err = await chain.add_block(header_blocks[507])
82
-- print(res, err)
83
-- assert res == AddBlockResult.DISCONNECTED_BLOCK
84
--
85
-- res, err = await chain.add_block(
86
-- dataclasses.replace(header_blocks[506], challenge_chain_ip_proof=VDFProof(2, b"123", True))
87
-- )
88
-- assert res == AddBlockResult.INVALID_BLOCK
89
--
90
-- assert (await chain.get_peak_block()).height == 505
91
--
92
-- for block in header_blocks[506:]:
93
-- res, err = await chain.add_block(block)
94
-- assert res == AddBlockResult.NEW_PEAK
95
-- assert (await chain.get_peak_block()).height == block.height
96
--
97
-- assert (await chain.get_peak_block()).height == 999
48
++ )
49
++ assert resp_3 is not None
50
++ weight_proof = full_node_protocol.RespondProofOfWeight.from_bytes(resp.data).wp
51
++ assert wallet_node._weight_proof_handler is not None
52
++ records = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof, True)
53
++ weight_proof_short = full_node_protocol.RespondProofOfWeight.from_bytes(resp_2.data).wp
54
++ records_short = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_short, True)
55
++ weight_proof_long = full_node_protocol.RespondProofOfWeight.from_bytes(resp_3.data).wp
56
++ records_long = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_long, True)
57
++
58
++ async with DBConnection(1) as db_wrapper:
59
++ store = await KeyValStore.create(db_wrapper)
60
++ chain = await WalletBlockchain.create(store, bt.constants)
61
++
62
++ assert (await chain.get_peak_block()) is None
63
++ assert chain.get_latest_timestamp() == 0
64
++
65
++ await chain.new_valid_weight_proof(weight_proof, records)
66
++ peak_block = await chain.get_peak_block()
67
++ assert peak_block is not None
68
++ assert peak_block.height == 499
69
++ assert chain.get_latest_timestamp() > 0
70
++
71
++ await chain.new_valid_weight_proof(weight_proof_short, records_short)
72
++ peak_block = await chain.get_peak_block()
73
++ assert peak_block is not None
74
++ assert peak_block.height == 499
75
++
76
++ await chain.new_valid_weight_proof(weight_proof_long, records_long)
77
++ peak_block = await chain.get_peak_block()
78
++ assert peak_block is not None
79
++ assert peak_block.height == 505
80
++
81
++ header_blocks: List[HeaderBlock] = []
82
++ for block in default_1000_blocks:
83
++ header_block = get_block_header(block, [], [])
84
++ header_blocks.append(header_block)
85
++
86
++ res, err = await chain.add_block(header_blocks[50])
87
++ print(res, err)
88
++ assert res == AddBlockResult.DISCONNECTED_BLOCK
89
++
90
++ res, err = await chain.add_block(header_blocks[400])
91
++ print(res, err)
92
++ assert res == AddBlockResult.ALREADY_HAVE_BLOCK
93
++
94
++ res, err = await chain.add_block(header_blocks[507])
95
++ print(res, err)
96
++ assert res == AddBlockResult.DISCONNECTED_BLOCK
97
++
98
++ res, err = await chain.add_block(
99
++ dataclasses.replace(header_blocks[506], challenge_chain_ip_proof=VDFProof(uint8(2), b"123", True))
100
++ )
101
++ assert res == AddBlockResult.INVALID_BLOCK
102
++
103
++ peak_block = await chain.get_peak_block()
104
++ assert peak_block is not None
105
++ assert peak_block.height == 505
106
++
107
++ for header_block in header_blocks[506:]:
108
++ res, err = await chain.add_block(header_block)
109
++ assert res == AddBlockResult.NEW_PEAK
110
++ peak_block = await chain.get_peak_block()
111
++ assert peak_block is not None
112
++ assert peak_block.height == header_block.height
113
++
114
++ peak_block = await chain.get_peak_block()
115
++ assert peak_block is not None
116
++ assert peak_block.height == 999
tests/wallet/test_wallet_node.py CHANGED
@@@ -6,10 -6,10 +6,9 @@@ import sy
6
6
import time
7
7
import types
8
8
from pathlib import Path
9
-- from typing import Any, Dict, List, Optional
9
++ from typing import List, Optional
10
10

11
11
import pytest
12
-- from chia_rs import PrivateKey
13
12

14
13
from chia.protocols import wallet_protocol
15
14
from chia.protocols.protocol_message_types import ProtocolMessageTypes
@@@ -35,12 -35,12 +34,12 @@@ from tests.util.time_out_assert import
35
34

36
35
@pytest.mark.anyio
37
36
async def test_get_private_key(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
38
-- root_path: Path = root_path_populated_with_config
39
-- keychain: Keychain = get_temp_keyring
40
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
41
-- node: WalletNode = WalletNode(config, root_path, test_constants, keychain)
42
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
43
-- fingerprint: int = sk.get_g1().get_fingerprint()
37
++ root_path = root_path_populated_with_config
38
++ keychain = get_temp_keyring
39
++ config = load_config(root_path, "config.yaml", "wallet")
40
++ node = WalletNode(config, root_path, test_constants, keychain)
41
++ sk = keychain.add_private_key(generate_mnemonic())
42
++ fingerprint = sk.get_g1().get_fingerprint()
44
43

45
44
key = await node.get_private_key(fingerprint)
46
45

@@@ -50,12 -50,12 +49,12 @@@
50
49

51
50
@pytest.mark.anyio
52
51
async def test_get_private_key_default_key(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
53
-- root_path: Path = root_path_populated_with_config
54
-- keychain: Keychain = get_temp_keyring
55
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
56
-- node: WalletNode = WalletNode(config, root_path, test_constants, keychain)
57
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
58
-- fingerprint: int = sk.get_g1().get_fingerprint()
52
++ root_path = root_path_populated_with_config
53
++ keychain = get_temp_keyring
54
++ config = load_config(root_path, "config.yaml", "wallet")
55
++ node = WalletNode(config, root_path, test_constants, keychain)
56
++ sk = keychain.add_private_key(generate_mnemonic())
57
++ fingerprint = sk.get_g1().get_fingerprint()
59
58

60
59
# Add a couple more keys
61
60
keychain.add_private_key(generate_mnemonic())
@@@ -73,10 -73,10 +72,10 @@@
73
72
async def test_get_private_key_missing_key(
74
73
root_path_populated_with_config: Path, get_temp_keyring: Keychain, fingerprint: Optional[int]
75
74
) -> None:
76
-- root_path: Path = root_path_populated_with_config
77
-- keychain: Keychain = get_temp_keyring # empty keyring
78
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
79
-- node: WalletNode = WalletNode(config, root_path, test_constants, keychain)
75
++ root_path = root_path_populated_with_config
76
++ keychain = get_temp_keyring # empty keyring
77
++ config = load_config(root_path, "config.yaml", "wallet")
78
++ node = WalletNode(config, root_path, test_constants, keychain)
80
79

81
80
# Keyring is empty, so requesting a key by fingerprint or None should return None
82
81
key = await node.get_private_key(fingerprint)
@@@ -88,12 -88,12 +87,12 @@@
88
87
async def test_get_private_key_missing_key_use_default(
89
88
root_path_populated_with_config: Path, get_temp_keyring: Keychain
90
89
) -> None:
91
-- root_path: Path = root_path_populated_with_config
92
-- keychain: Keychain = get_temp_keyring
93
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
94
-- node: WalletNode = WalletNode(config, root_path, test_constants, keychain)
95
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
96
-- fingerprint: int = sk.get_g1().get_fingerprint()
90
++ root_path = root_path_populated_with_config
91
++ keychain = get_temp_keyring
92
++ config = load_config(root_path, "config.yaml", "wallet")
93
++ node = WalletNode(config, root_path, test_constants, keychain)
94
++ sk = keychain.add_private_key(generate_mnemonic())
95
++ fingerprint = sk.get_g1().get_fingerprint()
97
96

98
97
# Stupid sanity check that the fingerprint we're going to use isn't actually in the keychain
99
98
assert fingerprint != 1234567890
@@@ -106,12 -106,12 +105,12 @@@
106
105

107
106

108
107
def test_log_in(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
109
-- root_path: Path = root_path_populated_with_config
110
-- keychain: Keychain = get_temp_keyring
111
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
112
-- node: WalletNode = WalletNode(config, root_path, test_constants)
113
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
114
-- fingerprint: int = sk.get_g1().get_fingerprint()
108
++ root_path = root_path_populated_with_config
109
++ keychain = get_temp_keyring
110
++ config = load_config(root_path, "config.yaml", "wallet")
111
++ node = WalletNode(config, root_path, test_constants)
112
++ sk = keychain.add_private_key(generate_mnemonic())
113
++ fingerprint = sk.get_g1().get_fingerprint()
115
114

116
115
node.log_in(sk)
117
116

@@@ -121,23 -121,23 +120,23 @@@
121
120

122
121

123
122
def test_log_in_failure_to_write_last_used_fingerprint(
124
-- root_path_populated_with_config: Path, get_temp_keyring: Keychain, monkeypatch: Any
123
++ root_path_populated_with_config: Path, get_temp_keyring: Keychain, monkeypatch: pytest.MonkeyPatch
125
124
) -> None:
126
125
called_update_last_used_fingerprint: bool = False
127
126

128
-- def patched_update_last_used_fingerprint(self: Any) -> None:
127
++ def patched_update_last_used_fingerprint(self: Self) -> None:
129
128
nonlocal called_update_last_used_fingerprint
130
129
called_update_last_used_fingerprint = True
131
130
raise Exception("Generic write failure")
132
131

133
132
with monkeypatch.context() as m:
134
133
m.setattr(WalletNode, "update_last_used_fingerprint", patched_update_last_used_fingerprint)
135
-- root_path: Path = root_path_populated_with_config
136
-- keychain: Keychain = get_temp_keyring
137
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
138
-- node: WalletNode = WalletNode(config, root_path, test_constants)
139
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
140
-- fingerprint: int = sk.get_g1().get_fingerprint()
134
++ root_path = root_path_populated_with_config
135
++ keychain = get_temp_keyring
136
++ config = load_config(root_path, "config.yaml", "wallet")
137
++ node = WalletNode(config, root_path, test_constants)
138
++ sk = keychain.add_private_key(generate_mnemonic())
139
++ fingerprint = sk.get_g1().get_fingerprint()
141
140

142
141
# Expect log_in to succeed, even though we can't write the last used fingerprint
143
142
node.log_in(sk)
@@@ -149,12 -149,12 +148,12 @@@
149
148

150
149

151
150
def test_log_out(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
152
-- root_path: Path = root_path_populated_with_config
153
-- keychain: Keychain = get_temp_keyring
154
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
155
-- node: WalletNode = WalletNode(config, root_path, test_constants)
156
-- sk: PrivateKey = keychain.add_private_key(generate_mnemonic())
157
-- fingerprint: int = sk.get_g1().get_fingerprint()
151
++ root_path = root_path_populated_with_config
152
++ keychain = get_temp_keyring
153
++ config = load_config(root_path, "config.yaml", "wallet")
154
++ node = WalletNode(config, root_path, test_constants)
155
++ sk = keychain.add_private_key(generate_mnemonic())
156
++ fingerprint = sk.get_g1().get_fingerprint()
158
157

159
158
node.log_in(sk)
160
159

@@@ -170,32 -170,32 +169,32 @@@
170
169

171
170

172
171
def test_get_last_used_fingerprint_path(root_path_populated_with_config: Path) -> None:
173
-- root_path: Path = root_path_populated_with_config
174
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
175
-- node: WalletNode = WalletNode(config, root_path, test_constants)
176
-- path: Optional[Path] = node.get_last_used_fingerprint_path()
172
++ root_path = root_path_populated_with_config
173
++ config = load_config(root_path, "config.yaml", "wallet")
174
++ node = WalletNode(config, root_path, test_constants)
175
++ path = node.get_last_used_fingerprint_path()
177
176

178
177
assert path == root_path / "wallet" / "db" / "last_used_fingerprint"
179
178

180
179

181
180
def test_get_last_used_fingerprint(root_path_populated_with_config: Path) -> None:
182
-- path: Path = root_path_populated_with_config / "wallet" / "db" / "last_used_fingerprint"
181
++ path = root_path_populated_with_config / "wallet" / "db" / "last_used_fingerprint"
183
182
path.parent.mkdir(parents=True, exist_ok=True)
184
183
path.write_text("1234567890")
185
184

186
-- root_path: Path = root_path_populated_with_config
187
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
188
-- node: WalletNode = WalletNode(config, root_path, test_constants)
189
-- last_used_fingerprint: Optional[int] = node.get_last_used_fingerprint()
185
++ root_path = root_path_populated_with_config
186
++ config = load_config(root_path, "config.yaml", "wallet")
187
++ node = WalletNode(config, root_path, test_constants)
188
++ last_used_fingerprint = node.get_last_used_fingerprint()
190
189

191
190
assert last_used_fingerprint == 1234567890
192
191

193
192

194
193
def test_get_last_used_fingerprint_file_doesnt_exist(root_path_populated_with_config: Path) -> None:
195
-- root_path: Path = root_path_populated_with_config
196
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
197
-- node: WalletNode = WalletNode(config, root_path, test_constants)
198
-- last_used_fingerprint: Optional[int] = node.get_last_used_fingerprint()
194
++ root_path = root_path_populated_with_config
195
++ config = load_config(root_path, "config.yaml", "wallet")
196
++ node = WalletNode(config, root_path, test_constants)
197
++ last_used_fingerprint = node.get_last_used_fingerprint()
199
198

200
199
assert last_used_fingerprint is None
201
200

@@@ -204,10 -204,10 +203,10 @@@ def test_get_last_used_fingerprint_file
204
203
if sys.platform in ["win32", "cygwin"]:
205
204
pytest.skip("Setting UNIX file permissions doesn't apply to Windows")
206
205

207
-- root_path: Path = root_path_populated_with_config
208
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
209
-- node: WalletNode = WalletNode(config, root_path, test_constants)
210
-- path: Path = node.get_last_used_fingerprint_path()
206
++ root_path = root_path_populated_with_config
207
++ config = load_config(root_path, "config.yaml", "wallet")
208
++ node = WalletNode(config, root_path, test_constants)
209
++ path = node.get_last_used_fingerprint_path()
211
210
path.parent.mkdir(parents=True, exist_ok=True)
212
211
path.write_text("1234567890")
213
212

@@@ -216,7 -216,7 +215,7 @@@
216
215
# Make the file unreadable
217
216
path.chmod(0o000)
218
217

219
-- last_used_fingerprint: Optional[int] = node.get_last_used_fingerprint()
218
++ last_used_fingerprint = node.get_last_used_fingerprint()
220
219

221
220
assert last_used_fingerprint is None
222
221

@@@ -231,22 -231,22 +230,22 @@@
231
230

232
231

233
232
def test_get_last_used_fingerprint_file_cant_read_win32(
234
-- root_path_populated_with_config: Path, monkeypatch: Any
233
++ root_path_populated_with_config: Path, monkeypatch: pytest.MonkeyPatch
235
234
) -> None:
236
235
if sys.platform not in ["win32", "cygwin"]:
237
236
pytest.skip("Windows-specific test")
238
237

239
-- called_read_text: bool = False
238
++ called_read_text = False
240
239

241
-- def patched_pathlib_path_read_text(self: Any) -> str:
240
++ def patched_pathlib_path_read_text(self: Self) -> str:
242
241
nonlocal called_read_text
243
242
called_read_text = True
244
243
raise PermissionError("Permission denied")
245
244

246
-- root_path: Path = root_path_populated_with_config
247
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
248
-- node: WalletNode = WalletNode(config, root_path, test_constants)
249
-- path: Path = node.get_last_used_fingerprint_path()
245
++ root_path = root_path_populated_with_config
246
++ config = load_config(root_path, "config.yaml", "wallet")
247
++ node = WalletNode(config, root_path, test_constants)
248
++ path = node.get_last_used_fingerprint_path()
250
249
path.parent.mkdir(parents=True, exist_ok=True)
251
250
path.write_text("1234567890")
252
251

@@@ -268,10 -268,10 +267,10 @@@
268
267

269
268

270
269
def test_get_last_used_fingerprint_file_with_whitespace(root_path_populated_with_config: Path) -> None:
271
-- root_path: Path = root_path_populated_with_config
272
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
273
-- node: WalletNode = WalletNode(config, root_path, test_constants)
274
-- path: Path = node.get_last_used_fingerprint_path()
270
++ root_path = root_path_populated_with_config
271
++ config = load_config(root_path, "config.yaml", "wallet")
272
++ node = WalletNode(config, root_path, test_constants)
273
++ path = node.get_last_used_fingerprint_path()
275
274
path.parent.mkdir(parents=True, exist_ok=True)
276
275
path.write_text("\n\r\n \t1234567890\r\n\n")
277
276

@@@ -279,9 -279,9 +278,9 @@@
279
278

280
279

281
280
def test_update_last_used_fingerprint_missing_fingerprint(root_path_populated_with_config: Path) -> None:
282
-- root_path: Path = root_path_populated_with_config
283
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
284
-- node: WalletNode = WalletNode(config, root_path, test_constants)
281
++ root_path = root_path_populated_with_config
282
++ config = load_config(root_path, "config.yaml", "wallet")
283
++ node = WalletNode(config, root_path, test_constants)
285
284
node.logged_in_fingerprint = None
286
285

287
286
with pytest.raises(AssertionError):
@@@ -289,9 -289,9 +288,9 @@@
289
288

290
289

291
290
def test_update_last_used_fingerprint_create_intermediate_dirs(root_path_populated_with_config: Path) -> None:
292
-- root_path: Path = root_path_populated_with_config
293
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
294
-- node: WalletNode = WalletNode(config, root_path, test_constants)
291
++ root_path = root_path_populated_with_config
292
++ config = load_config(root_path, "config.yaml", "wallet")
293
++ node = WalletNode(config, root_path, test_constants)
295
294
node.logged_in_fingerprint = 9876543210
296
295
path = node.get_last_used_fingerprint_path()
297
296

@@@ -303,9 -303,9 +302,9 @@@
303
302

304
303

305
304
def test_update_last_used_fingerprint(root_path_populated_with_config: Path) -> None:
306
-- root_path: Path = root_path_populated_with_config
307
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
308
-- node: WalletNode = WalletNode(config, root_path, test_constants)
305
++ root_path = root_path_populated_with_config
306
++ config = load_config(root_path, "config.yaml", "wallet")
307
++ node = WalletNode(config, root_path, test_constants)
309
308
node.logged_in_fingerprint = 9876543210
310
309
path = node.get_last_used_fingerprint_path()
311
310

@@@ -318,8 -318,8 +317,8 @@@
318
317
@pytest.mark.parametrize("testing", [True, False])
319
318
@pytest.mark.parametrize("offset", [0, 550, 650])
320
319
def test_timestamp_in_sync(root_path_populated_with_config: Path, testing: bool, offset: int) -> None:
321
-- root_path: Path = root_path_populated_with_config
322
-- config: Dict[str, Any] = load_config(root_path, "config.yaml", "wallet")
320
++ root_path = root_path_populated_with_config
321
++ config = load_config(root_path, "config.yaml", "wallet")
323
322
wallet_node = WalletNode(config, root_path, test_constants)
324
323
now = time.time()
325
324
wallet_node.config["testing"] = testing
@@@ -353,8 -353,8 +352,8 @@@ async def test_get_timestamp_for_height
353
352
# Clear the cache and add the peak back with a modified timestamp
354
353
cache = wallet_node.get_cache_for_peer(full_node_peer)
355
354
cache.clear_after_height(0)
356
-- modified_foliage_transaction_block = dataclasses.replace(
357
-- block_at_peak.foliage_transaction_block, timestamp=uint64(timestamp_at_peak + 1)
355
++ modified_foliage_transaction_block = block_at_peak.foliage_transaction_block.replace(
356
++ timestamp=uint64(timestamp_at_peak + 1)
358
357
)
359
358
modified_peak = dataclasses.replace(peak, foliage_transaction_block=modified_foliage_transaction_block)
360
359
cache.add_to_blocks(modified_peak)
@@@ -385,7 -385,7 +384,7 @@@
385
384

386
385
@pytest.mark.anyio
387
386
async def test_unique_puzzle_hash_subscriptions(simulator_and_wallet: OldSimulatorsAndWallets) -> None:
388
-- _, [(node, _)], bt = simulator_and_wallet
387
++ _, [(node, _)], _ = simulator_and_wallet
389
388
puzzle_hashes = await node.get_puzzle_hashes_to_subscribe()
390
389
assert len(puzzle_hashes) > 1
391
390
assert len(set(puzzle_hashes)) == len(puzzle_hashes)
@@@ -557,7 -557,7 +556,7 @@@ async def test_transaction_send_cache
557
556
await time_out_assert(5, logged_spends_len, 2)
558
557

559
558
# Tell the wallet that we recieved the spend (but failed to process it so it should send again)
560
-- msg: Message = make_msg(
559
++ msg = make_msg(
561
560
ProtocolMessageTypes.transaction_ack,
562
561
wallet_protocol.TransactionAck(tx.name, uint8(MempoolInclusionStatus.FAILED), Err.GENERATOR_RUNTIME_ERROR.name),
563
562
)
tests/wallet/test_wallet_user_store.py CHANGED
@@@ -8,7 -8,7 +8,7 @@@ from tests.util.db_connection import DB
8
8

9
9

10
10
@pytest.mark.anyio
11
-- async def test_store():
11
++ async def test_store() -> None:
12
12
async with DBConnection(1) as db_wrapper:
13
13
store = await WalletUserStore.create(db_wrapper)
14
14
await store.init_wallet()
@@@ -17,6 -17,6 +17,7 @@@
17
17
assert (await store.get_last_wallet()).id == i
18
18
wallet = await store.create_wallet("CAT_WALLET", WalletType.CAT, "abc")
19
19
assert wallet.id == i + 1
20
++ assert wallet is not None
20
21
assert wallet.id == 5
21
22

22
23
for i in range(2, 6):
tools/generate_chain.py CHANGED
@@@ -18,7 -18,7 +18,7 @@@ from chia.types.blockchain_format.coin
18
18
from chia.types.spend_bundle import SpendBundle
19
19
from chia.util.chia_logging import initialize_logging
20
20
from chia.util.ints import uint32, uint64
21
-- from tools.test_constants import test_constants
21
++ from tests.util.constants import test_constants
22
22

23
23

24
24
@contextmanager
tools/run_block.py CHANGED
@@@ -38,155 -38,155 +38,14 @@@ and in this way they control whether a
38
38
from __future__ import annotations
39
39

40
40
import json
41
-- from dataclasses import dataclass
42
41
from pathlib import Path
43
-- from typing import Dict, List, Tuple
44
42

45
43
import click
46
44

47
-- from chia.consensus.constants import ConsensusConstants
48
45
from chia.consensus.default_constants import DEFAULT_CONSTANTS
49
-- from chia.types.blockchain_format.coin import Coin
50
-- from chia.types.blockchain_format.serialized_program import SerializedProgram
51
-- from chia.types.blockchain_format.sized_bytes import bytes32
52
-- from chia.types.condition_opcodes import ConditionOpcode
53
-- from chia.types.condition_with_args import ConditionWithArgs
54
-- from chia.types.generator_types import BlockGenerator
55
46
from chia.util.config import load_config
56
47
from chia.util.default_root import DEFAULT_ROOT_PATH
57
-- from chia.util.ints import uint32, uint64
58
-- from chia.wallet.cat_wallet.cat_utils import match_cat_puzzle
59
-- from chia.wallet.puzzles.load_clvm import load_serialized_clvm_maybe_recompile
60
-- from chia.wallet.uncurried_puzzle import uncurry_puzzle
61
--
62
-- DESERIALIZE_MOD = load_serialized_clvm_maybe_recompile(
63
-- "chialisp_deserialisation.clsp", package_or_requirement="chia.consensus.puzzles"
64
-- )
65
--
66
--
67
-- @dataclass
68
-- class NPC:
69
-- coin_name: bytes32
70
-- puzzle_hash: bytes32
71
-- conditions: List[Tuple[ConditionOpcode, List[ConditionWithArgs]]]
72
--
73
--
74
-- @dataclass
75
-- class CAT:
76
-- asset_id: str
77
-- memo: str
78
-- npc: NPC
79
--
80
-- def cat_to_dict(self):
81
-- return {"asset_id": self.asset_id, "memo": self.memo, "npc": npc_to_dict(self.npc)}
82
--
83
--
84
-- def condition_with_args_to_dict(condition_with_args: ConditionWithArgs):
85
-- return {
86
-- "condition_opcode": condition_with_args.opcode.name,
87
-- "arguments": [arg.hex() for arg in condition_with_args.vars],
88
-- }
89
--
90
--
91
-- def condition_list_to_dict(condition_list: Tuple[ConditionOpcode, List[ConditionWithArgs]]):
92
-- assert all([condition_list[0] == cwa.opcode for cwa in condition_list[1]])
93
-- return [condition_with_args_to_dict(cwa) for cwa in condition_list[1]]
94
--
95
--
96
-- def npc_to_dict(npc: NPC):
97
-- return {
98
-- "coin_name": npc.coin_name.hex(),
99
-- "conditions": [{"condition_type": c[0].name, "conditions": condition_list_to_dict(c)} for c in npc.conditions],
100
-- "puzzle_hash": npc.puzzle_hash.hex(),
101
-- }
102
--
103
--
104
-- def run_generator(block_generator: BlockGenerator, constants: ConsensusConstants, max_cost: int) -> List[CAT]:
105
-- block_args = [bytes(a) for a in block_generator.generator_refs]
106
-- cost, block_result = block_generator.program.run_with_cost(max_cost, [DESERIALIZE_MOD, block_args])
107
--
108
-- coin_spends = block_result.first()
109
--
110
-- cat_list: List[CAT] = []
111
-- for spend in coin_spends.as_iter():
112
-- parent, puzzle, amount, solution = spend.as_iter()
113
-- args = match_cat_puzzle(uncurry_puzzle(puzzle))
114
--
115
-- if args is None:
116
-- continue
117
--
118
-- _, asset_id, _ = args
119
-- memo = ""
120
--
121
-- puzzle_result = puzzle.run(solution)
122
--
123
-- conds: Dict[ConditionOpcode, List[ConditionWithArgs]] = {}
124
--
125
-- for condition in puzzle_result.as_python():
126
-- op = ConditionOpcode(condition[0])
127
--
128
-- if op not in conds:
129
-- conds[op] = []
130
--
131
-- if condition[0] != ConditionOpcode.CREATE_COIN or len(condition) < 4:
132
-- conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
133
-- continue
134
--
135
-- # If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount
136
-- if type(condition[3]) is not list:
137
-- # If it's not a list, it's not the correct format
138
-- conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
139
-- continue
140
--
141
-- conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]] + [condition[3][0]]))
142
--
143
-- # special retirement address
144
-- if condition[3][0].hex() != "0000000000000000000000000000000000000000000000000000000000000000":
145
-- continue
146
--
147
-- if len(condition[3]) >= 2:
148
-- try:
149
-- memo = condition[3][1].decode("utf-8", errors="strict")
150
-- except UnicodeError:
151
-- pass # ignore this error which should leave memo as empty string
152
--
153
-- # technically there could be more such create_coin ops in the list but our wallet does not
154
-- # so leaving it for the future
155
-- break
156
--
157
-- puzzle_hash = puzzle.get_tree_hash()
158
-- coin = Coin(bytes32(parent.as_atom()), puzzle_hash, amount.as_int())
159
-- cat_list.append(
160
-- CAT(
161
-- asset_id=bytes(asset_id).hex()[2:],
162
-- memo=memo,
163
-- npc=NPC(coin.name(), puzzle_hash, [(op, cond) for op, cond in conds.items()]),
164
-- )
165
-- )
166
--
167
-- return cat_list
168
--
169
--
170
-- def ref_list_to_args(ref_list: List[uint32], root_path: Path) -> List[SerializedProgram]:
171
-- args = []
172
-- for height in ref_list:
173
-- with open(root_path / f"{height}.json", "rb") as f:
174
-- program_str = json.load(f)["block"]["transactions_generator"]
175
-- args.append(SerializedProgram.fromhex(program_str))
176
-- return args
177
--
178
--
179
-- def run_generator_with_args(
180
-- generator_program_hex: str,
181
-- generator_args: List[SerializedProgram],
182
-- constants: ConsensusConstants,
183
-- cost: uint64,
184
-- ) -> List[CAT]:
185
-- if not generator_program_hex:
186
-- return []
187
-- generator_program = SerializedProgram.fromhex(generator_program_hex)
188
-- block_generator = BlockGenerator(generator_program, generator_args, [])
189
-- return run_generator(block_generator, constants, min(constants.MAX_BLOCK_COST_CLVM, cost))
48
++ from tests.util.run_block import run_json_block
190
49

191
50

192
51
@click.command()
@@@ -196,19 -196,19 +55,6 @@@ def cmd_run_json_block_file(filename)
196
55
return run_json_block_file(Path(filename))
197
56

198
57

199
-- def run_json_block(full_block, parent: Path, constants: ConsensusConstants) -> List[CAT]:
200
-- ref_list = full_block["block"]["transactions_generator_ref_list"]
201
-- tx_info: dict = full_block["block"]["transactions_info"]
202
-- generator_program_hex: str = full_block["block"]["transactions_generator"]
203
-- cat_list: List[CAT] = []
204
-- if tx_info and generator_program_hex:
205
-- cost = tx_info["cost"]
206
-- args = ref_list_to_args(ref_list, parent)
207
-- cat_list = run_generator_with_args(generator_program_hex, args, constants, cost)
208
--
209
-- return cat_list
210
--
211
--
212
58
def run_json_block_file(filename: Path):
213
59
full_block = json.load(filename.open("rb"))
214
60
# pull in current constants from config.yaml
tools/test_full_sync.py CHANGED
@@@ -3,15 -3,15 +3,9 @@@
3
3
from __future__ import annotations
4
4

5
5
import asyncio
6
-- import cProfile
7
-- import logging
8
6
import os
9
-- import shutil
10
-- import tempfile
11
-- import time
12
-- from contextlib import contextmanager
13
7
from pathlib import Path
14
-- from typing import Callable, Iterator, List, Optional, cast
8
++ from typing import Optional
15
9

16
10
import aiosqlite
17
11
import click
@@@ -20,213 -20,213 +14,10 @@@ import zst
20
14
from chia.cmds.init_funcs import chia_init
21
15
from chia.consensus.default_constants import DEFAULT_CONSTANTS
22
16
from chia.full_node.full_node import FullNode
23
-- from chia.server.outbound_message import Message, NodeType
24
-- from chia.server.server import ChiaServer
25
17
from chia.server.ws_connection import WSChiaConnection
26
-- from chia.simulator.block_tools import make_unfinished_block
27
-- from chia.types.blockchain_format.sized_bytes import bytes32
28
18
from chia.types.full_block import FullBlock
29
-- from chia.types.peer_info import PeerInfo
30
19
from chia.util.config import load_config
31
-- from chia.util.ints import uint16
32
-- from tools.test_constants import test_constants as TEST_CONSTANTS
33
--
34
--
35
-- class ExitOnError(logging.Handler):
36
-- def __init__(self):
37
-- super().__init__()
38
-- self.exit_with_failure = False
39
--
40
-- def emit(self, record):
41
-- if record.levelno != logging.ERROR:
42
-- return
43
-- self.exit_with_failure = True
44
--
45
--
46
-- @contextmanager
47
-- def enable_profiler(profile: bool, counter: int) -> Iterator[None]:
48
-- if not profile:
49
-- yield
50
-- return
51
--
52
-- with cProfile.Profile() as pr:
53
-- receive_start_time = time.monotonic()
54
-- yield
55
--
56
-- if time.monotonic() - receive_start_time > 5:
57
-- pr.create_stats()
58
-- pr.dump_stats(f"slow-batch-{counter:05d}.profile")
59
--
60
--
61
-- class FakeServer:
62
-- async def send_to_all(self, messages: List[Message], node_type: NodeType, exclude: Optional[bytes32] = None):
63
-- pass
64
--
65
-- def set_received_message_callback(self, callback: Callable):
66
-- pass
67
--
68
-- async def get_peer_info(self) -> Optional[PeerInfo]:
69
-- return None
70
--
71
-- def get_connections(
72
-- self, node_type: Optional[NodeType] = None, *, outbound: Optional[bool] = False
73
-- ) -> List[WSChiaConnection]:
74
-- return []
75
--
76
-- def is_duplicate_or_self_connection(self, target_node: PeerInfo) -> bool:
77
-- return False
78
--
79
-- async def start_client(
80
-- self,
81
-- target_node: PeerInfo,
82
-- on_connect: Callable = None,
83
-- auth: bool = False,
84
-- is_feeler: bool = False,
85
-- ) -> bool:
86
-- return False
87
--
88
--
89
-- class FakePeer:
90
-- def get_peer_logging(self) -> PeerInfo:
91
-- return PeerInfo("0.0.0.0", uint16(0))
92
--
93
-- def __init__(self):
94
-- self.peer_node_id = bytes([0] * 32)
95
--
96
-- async def get_peer_info(self) -> Optional[PeerInfo]:
97
-- return None
98
--
99
--
100
-- async def run_sync_test(
101
-- file: Path,
102
-- db_version,
103
-- profile: bool,
104
-- single_thread: bool,
105
-- test_constants: bool,
106
-- keep_up: bool,
107
-- db_sync: str,
108
-- node_profiler: bool,
109
-- start_at_checkpoint: Optional[str],
110
-- ) -> None:
111
-- logger = logging.getLogger()
112
-- logger.setLevel(logging.WARNING)
113
-- handler = logging.FileHandler("test-full-sync.log")
114
-- handler.setFormatter(
115
-- logging.Formatter(
116
-- "%(levelname)-8s %(message)s",
117
-- datefmt="%Y-%m-%dT%H:%M:%S",
118
-- )
119
-- )
120
-- logger.addHandler(handler)
121
-- check_log = ExitOnError()
122
-- logger.addHandler(check_log)
123
--
124
-- with tempfile.TemporaryDirectory() as root_dir:
125
-- root_path = Path(root_dir, "root")
126
-- if start_at_checkpoint is not None:
127
-- shutil.copytree(start_at_checkpoint, root_path)
128
--
129
-- chia_init(root_path, should_check_keys=False, v1_db=(db_version == 1))
130
-- config = load_config(root_path, "config.yaml")
131
--
132
-- if test_constants:
133
-- constants = TEST_CONSTANTS
134
-- else:
135
-- overrides = config["network_overrides"]["constants"][config["selected_network"]]
136
-- constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
137
-- if single_thread:
138
-- config["full_node"]["single_threaded"] = True
139
-- config["full_node"]["db_sync"] = db_sync
140
-- config["full_node"]["enable_profiler"] = node_profiler
141
-- full_node = await FullNode.create(
142
-- config["full_node"],
143
-- root_path=root_path,
144
-- consensus_constants=constants,
145
-- )
146
--
147
-- full_node.set_server(cast(ChiaServer, FakeServer()))
148
-- async with full_node.manage():
149
-- peak = full_node.blockchain.get_peak()
150
-- if peak is not None:
151
-- height = int(peak.height)
152
-- else:
153
-- height = 0
154
--
155
-- peer: WSChiaConnection = cast(WSChiaConnection, FakePeer())
156
--
157
-- print()
158
-- counter = 0
159
-- monotonic = height
160
-- prev_hash = None
161
-- async with aiosqlite.connect(file) as in_db:
162
-- await in_db.execute("pragma query_only")
163
-- rows = await in_db.execute(
164
-- "SELECT header_hash, height, block FROM full_blocks "
165
-- "WHERE height >= ? AND in_main_chain=1 ORDER BY height",
166
-- (height,),
167
-- )
168
--
169
-- block_batch = []
170
--
171
-- start_time = time.monotonic()
172
-- logger.warning(f"starting test {start_time}")
173
-- worst_batch_height = None
174
-- worst_batch_time_per_block = None
175
-- peer_info = peer.get_peer_logging()
176
-- async for r in rows:
177
-- batch_start_time = time.monotonic()
178
-- with enable_profiler(profile, height):
179
-- block = FullBlock.from_bytes(zstd.decompress(r[2]))
180
-- block_batch.append(block)
181
--
182
-- assert block.height == monotonic
183
-- monotonic += 1
184
-- assert prev_hash is None or block.prev_header_hash == prev_hash
185
-- prev_hash = block.header_hash
186
--
187
-- if len(block_batch) < 32:
188
-- continue
189
--
190
-- if keep_up:
191
-- for b in block_batch:
192
-- await full_node.add_unfinished_block(make_unfinished_block(b, constants), peer)
193
-- await full_node.add_block(b)
194
-- else:
195
-- success, summary, _ = await full_node.add_block_batch(block_batch, peer_info, None)
196
-- end_height = block_batch[-1].height
197
-- full_node.blockchain.clean_block_record(end_height - full_node.constants.BLOCKS_CACHE_SIZE)
198
--
199
-- if not success:
200
-- raise RuntimeError("failed to ingest block batch")
201
--
202
-- assert summary is not None
203
--
204
-- time_per_block = (time.monotonic() - batch_start_time) / len(block_batch)
205
-- if not worst_batch_height or worst_batch_time_per_block > time_per_block:
206
-- worst_batch_height = height
207
-- worst_batch_time_per_block = time_per_block
208
--
209
-- counter += len(block_batch)
210
-- height += len(block_batch)
211
-- print(
212
-- f"\rheight {height} {time_per_block:0.2f} s/block ",
213
-- end="",
214
-- )
215
-- block_batch = []
216
-- if check_log.exit_with_failure:
217
-- raise RuntimeError("error printed to log. exiting")
218
--
219
-- if counter >= 100000:
220
-- counter = 0
221
-- print()
222
-- end_time = time.monotonic()
223
-- logger.warning(f"test completed at {end_time}")
224
-- logger.warning(f"duration: {end_time - start_time:0.2f} s")
225
-- logger.warning(f"worst time-per-block: {worst_batch_time_per_block:0.2f} s")
226
-- logger.warning(f"worst height: {worst_batch_height}")
227
-- logger.warning(f"end-height: {height}")
228
-- if node_profiler:
229
-- (root_path / "profile-node").rename("./profile-node")
20
++ from tests.util.full_sync import FakePeer, FakeServer, run_sync_test
230
21

231
22

232
23
@click.group()